Hexo 迁移到 Hugo 记录
参考:
为什么要迁移到Hugo?
因为 Hexo 太慢啦~ Node的依赖也乱七八糟~ 顺便想要简洁主题找不到合适自己的,要么太过简洁,想要的功能都没有;要么太过臃肿,阅读界面太乱;太难了~
迁移需要注意的地方
关于 Front-matter
时间格式 date
Hexo 中时间格式与 Hugo 格式不太相同,Hugo 对时间格式的要求比较严格,Hexo 中YYYY-MM-DD HH:MM:SS
格式的时间在 Hugo 中无法被正确识别。
|
|
最后的+08:00
代表中国的时区是 GMT+8。
另外,Hexo 中 Front Matter 代表博客更新时间的updated
选项,在 Hugo 中是lastmod
。
由于写的文章太多,我直接用正则表达式搜索替换:
|
|
关于迁移旧链接
迁移博客过程中最重要的莫过于保证文章的 URL 不变,不然,这将会非常不利于 SEO。好不容易有一篇文章出现在 Google 相关搜索结果的前排,却因为 URL 的变化导致原链接 404 从而导致该文章从 Google 的索引中移除,这一定会是非常令人沮丧的😶。下面我们就来说说怎么解决这个问题~
Hugo 默认使用 “Pretty URLs”(漂亮URL), 而 Hexo 使用 “Ugly URLs”(丑陋URL)
|
|
“_index.md” 和 “index.md” 文件在 Hugo 中具有特殊含义(用于组织页面)。在 “Pretty URLs” 渲染下,与实际路径一致。
若文件名不为 “_index.md” 或 “index.md” 时,“Pretty URLs” 将 md 文件渲染为目录。
会导致后果:md 文件引用本地图片时,渲染路径比实际路径多了一个与文件名同名的目录名,导致引用图片失败。
而 “Ugly URLs” 则会直接渲染为 xxx.html,这会导致迁移后 Hexo 版本的链接会直接失效。若有搜索引擎收录了 Hexo 版本的链接,则会指向 404 页面。
这是我不能接受的,于是我翻了翻 Hugo 文档,终于找到了解决方法:
假设您在content/posts/my-awesome-blog-post.md
中创建了一条新内容。内容是您以前的帖子的修订版,位于content/posts/my-original-url.md
。您可以在新的my-awesome-blog-post.md
的开头创建别名字段,在其中可以添加以前的路径。以下示例说明如何在 YAML 中创建此字段。(机翻勿吐槽🙈️……)
content/posts/my-awesome-post.md
|
|
当你输入aliases
中任意一个路径时,他就会跳转到my-awesome-post.md
所在的页面啦~
给 Hugo 添加新功能
永久链接
之前使用 Hexo 的时候,用的是hexo-abbrlink
插件来处理永久链接。迁移到 Hugo 之后,发现没有这种插件,顿时就慌了,那可怎么办呀~
搜索了一下发现还是有大佬利用 Hugo 自建函数实现了这个功能。
永久链接生成方案
大佬的永久链接生成方案是直接对时间 + 文章名生成字符串做一下 md5 然后取任意 4-12 位。想了一下,这样的 hash 冲撞概率还是挺小的,我觉得可以!
那么接下来说说怎么把这个方案应用到 Hugo 中……
Hugo 在永久链接中支持一个参数:slug
。简单来说,我们可以针对每一篇文章指定一个 slug
,然后在 config.toml
中配置permalinks
包含slug
参数,就可以生成唯一的永久链接。我们的目的就是对每篇文章自动生成一个 slug。
修改archetypes/default.md
添加如下一行:
|
|
这样在每次使用hugo new
的时候就会自动填写一个永久链接了。
之后修改config.toml
添加如下行:
|
|
DisqusJS
我现在用的是 LoveIt 主题,官方集成了 Disqus 评论模块,却没有集成 DisqusJS,于是决定自己动手~
以 LoveIt 主题为例:
拷贝
themes/LoveIt/layouts/partials/comment.html
到博客根目录/layouts/partials/comment.html
在
id="comments"
的 div 块里加入以下代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
<div id="comments"> ... {{- /* DisqusJS Comment System */ -}} {{- $disqusjs := $comment.disqusjs | default dict -}} {{- if $disqusjs.enable -}} <!-- jsDelivr --> {{- $source := $cdn.disqusjsCSS | default "lib/disqusjs/disqusjs.css" -}} <link rel="stylesheet" href="{{- $source -}}"> {{- $source := $cdn.disqusjsJS | default "lib/disqusjs/disqus.js" -}} <!-- jsDelivr --> <script src="{{- $source -}}"></script> <div id="disqus_thread" class="comment"></div> <script> var dsqjs = new DisqusJS({ shortname: {{- $disqusjs.shortname -}}, siteName: {{- $disqusjs.siteName -}}, api: {{- $disqusjs.api -}}, apikey: {{- $disqusjs.apikey -}}, admin: {{- $disqusjs.admin -}}, adminLabel: {{- $disqusjs.adminLabel -}} }); </script> {{- end -}} ... </div>
拷贝
themes/LoveIt/assets/data/cdn/jsdelivr.yml
到博客根目录/assets/data/cdn/jsdelivr.yml
在
libFiles
结点里添加以下代码:1 2 3 4
libFiles: ... disqusjsCSS: [email protected]1.3/dist/disqusjs.css disqusjsJS: [email protected]1.3/dist/disqus.js
修改
config.toml
添加如下行:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
[params.page.comment] enable = true ... # DisqusJS 评论系统设置 [params.page.comment.disqusjs] # Reborn 新增 enable = true # Disqus 的 shortname,用来在文章中启用 Disqus 评论系统 shortname = "Your shortname" siteName = 'Your siteName' apikey = 'Your apikey' api = 'https://disqus.skk.moe/disqus/' admin = 'Your admin' adminLabel = 'admin' ...
PWA/Service Worker
懒得写了,网上一堆解释,直接贴源码算了:
在
static/
目录下新建sw.js
文件,然后加入以下代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
importScripts('https://cdn.jsdelivr.net/npm/workbox-cdn/workbox/workbox-sw.js'); if (workbox) { console.log(`Yay! Workbox is loaded 🎉`); workbox.core.setCacheNameDetails({ prefix: "Your Username" }); workbox.core.skipWaiting(); workbox.core.clientsClaim(); workbox.precaching.precacheAndRoute([ { "url": "/index.html", "revision": "MD5 of your index.html" }, { "url": "/css/style.min.css", "revision": "MD5 of your style.css" } ], {}); workbox.routing.registerRoute(/(?:\/)$/, new workbox.strategies.StaleWhileRevalidate({ cacheName: "html", plugins: [ new workbox.expiration.ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 7, // purgeOnQuotaError: !0 }) ] }), "GET"); workbox.routing.registerRoute( /\.(?:js|css)$/, new workbox.strategies.StaleWhileRevalidate({ cacheName: 'static-resources' }) ) workbox.routing.registerRoute( /\.(?:png|jpg|jpeg|gif|bmp|webp|svg|ico)$/, new workbox.strategies.CacheFirst({ cacheName: "images", plugins: [new workbox.expiration.ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 7 * 24 * 60 * 60, // purgeOnQuotaError: !0 })] }), "GET"); // Fonts workbox.routing.registerRoute( /\.(?:eot|ttf|woff|woff2)$/, new workbox.strategies.CacheFirst({ cacheName: "fonts", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) ); workbox.routing.registerRoute( /^https:\/\/fonts\.googleapis\.com/, new workbox.strategies.StaleWhileRevalidate({ cacheName: 'google-fonts-stylesheets' }) ); workbox.routing.registerRoute( /^https:\/\/fonts\.gstatic\.com/, new workbox.strategies.CacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }), new workbox.expiration.ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 365, maxEntries: 30 }) ] }) ); // external resources workbox.routing.registerRoute( /(^https:\/\/cdn\.jsdelivr\.net.*?(\.js|\.css))|(^https:\/\/cdnjs\.cloudflare\.com)/, new workbox.strategies.CacheFirst({ cacheName: "external-resources", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) ); workbox.googleAnalytics.initialize({}); } else { console.log(`Boo! Workbox didn't load 😬`) }
拷贝
themes/LoveIt/layouts/_default/baseof.html
到博客根目录/layouts/_default/baseof.html
在
</body>
后加入以下代码:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<script> // 可以这么注册 Service Worker if ('serviceWorker' in navigator) { // 为了保证首屏渲染性能,可以在页面 load 完之后注册 Service Worker window.onload = function () { navigator.serviceWorker .register('/sw.js', { scope: '/' }) .then(function(registration) { console.log('Service Worker Registered'); }); navigator.serviceWorker .ready .then(function(registration) { console.log('Service Worker Ready'); }); }; } </script>
添加清理旧版本策略
修改sw.js
文件:
|
|
然后确保每个new workbox.strategies.xxx
的cacheName
都是如下代码:
|
|
Github Action 自动部署
Github Action 是个好东西,一定要好好利用,我已经写好一个 Hugo 部署到 github page 的 github action 了,直接用就好了~
在博客源码的 Github 仓库项目中
Settings—Secrets
设置好GH_TOKEN
、GIT_NAME
、GIT_EMAIL
新建
.github/workflows/deploy.yml
,示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
name: 自动部署 Hugo on: push: branches: - master jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [10.x] steps: - name: 开始运行 uses: actions/[email protected] - name: 缓存 uses: actions/[email protected] id: cache-dependencies with: path: node_modules key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}} - name: 部署准备 run: | git clone https://${{secrets.GH_TOKEN}}@github.com/username/username.github.io.git .deploy_git - name: 部署博客 uses: RebornQ/[email protected].1.2 with: github_token: ${{secrets.GH_TOKEN}} username: ${{secrets.GIT_NAME}} email: ${{secrets.GIT_EMAIL}} repo_url: https://${{secrets.GH_TOKEN}}@github.com/username/username.github.io.git
音乐 shortcode 支持本地歌词文件
太简单了,看 APlayer 文档就能找出来,直接贴代码:
拷贝
themes/LoveIt/layouts/shortcodes/music.html
到博客根目录/layouts/shortcodes/music.html
修改如下代码:
1 2 3 4 5 6 7 8 9
... {{- if .Get "url" -}} ... {{- $lrc := .Get "lrc" -}} {{- with dict "Path" $lrc "Resources" .Page.Resources | partial "function/resource.html" -}} {{- $lrc = .RelPermalink -}} {{- end -}} <meting-js ... lrc="{{ $lrc }}" ... ></meting-js> ...