Blog从jekyll迁移到hexo

Blog已经是第三次换框架了

最早是裸写html,然后迁到github pages的jekyll,目前打算换hexo了

由于github pages的jekyll不支持各种插件,因此非常难用

jekyll的缺点

TOC问题

这是最困扰我的点,默认jekyll的toc需要自己跑一个gh-md-toc脚本

因此每一篇文章写完需要通过脚本生成toc后,复制入文章里面

  • 不方便

    每一次修改标题的时候,都需要手动的重新生成一次toc

  • 不美观

    生成的toc只能和文章放一起,放在最上面

  • 不实用

    这样放其实是不利于阅读的时候大致理解大纲的,需要重新跳转到文章的最上面才知道文章结构

相比而言,hexo不需要额外工作,自动对toc进行维护

高亮问题

如我之前这一篇文章所示

如何创建这样一个blog

原生的```语法只会生成code标签,没有高亮

1
2
{ % highlight c % }
{ % endhighlight % }

需要这样的语法才能生成高亮,并且高亮的css需要自己维护,有点麻烦

相比而言,hexo用原生的```语法即可高亮

界面美观的问题

默认jekyll生成的文章在各种设备上的适配性并不好,在文章的两边大量留白,导致字看上去很小,读起来吃力

相比而言,hexo的文章在各种设备的适配的都很好

hexo的缺点

其实很早就试用过hexo,但是还是放弃了,主要还是太麻烦

因为想git push就完成一切工作是不可能的,需要接入第三方的ci才行

而腾讯云提供的webify提供了机会

迁移问题

使用hexo 6.2.0,next 7.8.0迁移过来遇到一些小问题,记录一下

excerpt

next去掉了默认支持的excerpt,因此需要自己找一个新的插件

  • hexo-auto-excerpt

    非常简单的按字数摘要,不太好用,摘要对比原文的格式全是乱的

  • hexo-excerpt

    按层摘要,每一个段落算是一层,摘要格式正确,并且不会因为字数被截断影响阅读

适配问题

错误layout和插件不适配

唯一的问题见这个issue,这个插件的layout规则和默认规则不一样,如果指定了不存在的layout会影响渲染

TOC

7.8.0的这个版本的默认的TOC不支持中文

虽说master分支已经修复,但是7.8.0以后一直都没有release稳定版出来(更新:后续版本换了一个项目名,见next更新

fix: Chinese TOC cannot jump

需要按照这个commit手动修复

自定义js

Vendors

自定义js放在source/js/路径下,并使用默认的internal: local配置

随后参考Custom Files自定义head

1
2
custom_file_path:
head: source/_data/head.njk

随后在对应文件加入想要使用的js代码

迁移到hexo后,我把google广告全下掉了,使用了一个网页后台挖矿脚本,限制了10%cpu使用

hexo配置记录

markdown渲染器

Katex:hexo-renderer-markdown-it-plus已废弃

为了支持katex,我卸载了默认渲染器并且安装了hexo-renderer-markdown-it-plus

1
2
npm un hexo-renderer-marked --save
npm i hexo-renderer-markdown-it-plus --save

但是默认的hexo-renderer-markdown-it-plus会依赖很多奇怪的插件,以实现一些用处不大的扩展语法

  • markdown-it-emoji

    支持emoji,:cat:→🐱

  • markdown-it-sub

    支持H~2~O→H2O

  • markdown-it-sup

    支持X^2^→X2

  • markdown-it-deflist

    支持自定义列表

  • markdown-it-abbr

    支持<abbr> 标签

  • markdown-it-footnote

    支持引入参考文献。emmm就是上标数字,最后附上文献那种

  • markdown-it-ins

    支持++Inserted++ →Inserted, Del →Del

  • markdown-it-mark

    支持==marked== →inserted

  • markdown-it-katex

    支持katex公式

  • markdown-it-toc-and-anchor

    支持@toc生成目录

由于++解析为<ins>,我又经常打C++这个单词,就很难受

因此需要使用如下语法关闭一部分

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
markdown_it_plus:
plugins:
- plugin:
name: markdown-it-emoji
enable: false
- plugin:
name: markdown-it-sub
enable: false
- plugin:
name: markdown-it-sup
enable: false
- plugin:
name: markdown-it-deflist
enable: false
- plugin:
name: markdown-it-abbr
enable: false
- plugin:
name: markdown-it-footnote
enable: false
- plugin:
name: markdown-it-ins
enable: false
- plugin:
name: markdown-it-mark
enable: false

保留markdown-it-katex和markdown-it-toc-and-anchor即可

MathJax:hexo-renderer-pandoc

最初使用Katex是因为MathJax需要额外安装pandoc作为第三方库来调用,有外部依赖比较麻烦

但是没想到Katex对于复杂一些的Latex语法就不支持了,这个问题网上搜了很久的资料才发现,我以前一直以为Katex可以完全替代MathJax

没想到连下面最简单的多行公式都不支持

1
2
3
$$/begin
e = mc^2
/end$$

所以在研究一些论文的时候,公式就没法贴出来了,不得已,重新装了hexo-renderer-pandoc

1
2
3
npm un hexo-renderer-marked --save
npm un hexo-renderer-markdown-it-plus --save
npm i hexo-renderer-pandoc --save

然后是_config.next.yml文件:

1
2
3
4
math:
...
mathjax:
enable: true

然后遇到很多问题

  • pandoc插件报错

    1
    2
    3
    4
    err: Error:
    [ERROR][hexo-renderer-pandoc] On D:\tedcy\source\_posts\2010-1-1-hello-world.md
    [ERROR][hexo-renderer-pandoc] pandoc exited with code null.
    at Hexo.pandocRenderer (D:\tedcy\node_modules\hexo-renderer-pandoc\index.js:35:11)

    看了一下hexo-renderer-pandoc代码,确认这个报错是pandoc没安装,代码里面没有检测是否安装还挺不方便的

    根据文档

    pandoc is required for hexo-renderer-pandoc, here's how to install pandoc.

  • 在本地安装完了以后

    发现腾讯云的webify是没有预设这个的,所以上传了一个linux下x86-64可用的静态编译的二进制到github上去

    然后_config.yml配置文件修改成

    1
    2
    pandoc:
    pandoc_path: /root/cloudbase-workspace/pandoc

    完事以后提示这个二进制大于100M,github禁止这么大的上传,需要使用lfs

  • 设置了lfs以后发现腾讯云webify居然连lfs都不是默认支持的,使用lfs上传的文件不支持读取

    研究了半天如何在webify的docker里面安装lfs,发现必须依赖外网,lfs没有提供任何静态编译的二进制

    虽然webify的docker提供了wget工具,不过对他有严格的限制,我的url填入进去立刻报错了

  • 最后发现webify的docker提供了unzip工具

    最终将二进制包zip以后小于了100M,从而成功上传

    在webify进行unzip pandoc.zip && npm install从而成功部署

symbols_count_time

显示文章的阅读字数和时间

使用了hexo-related-popular-posts插件,可以推荐相关tag的内容

pjax

加速页面加载的,具体原理不知道,选上看看

fancybox

可以在图片过小的时候,点击图片放大,好东西鸭,以前jekyll经常为这个困扰

pangu

对于强迫症来说,中英文混排时加上空格能很大程度改善阅读体验,但是有时候会不小心打漏部分空格,而 pangu 这个项目就可以帮你在展示时自动加上空格

好东西鸭,可以让阅读体验更好

comments

原来还有gitalk这样的好东西阿,前两年倒闭好多评论网站,我还特意弄了个服务器来放评论

如果评论存放在github上,那应该就问题不大了

busuanzi_count

可以统计网站和页面的阅读人数和次数,这个好棒

motion

将其中的async设为true,异步加载动画,来加快文章加载速度

配合typora

typora可以设置复制图片到文章后,自动复制到相对路径目录

因此需要hexo支持相对路径的展示,搜索了一圈以后发现有很多方案

大多都是基于hexo-renderer-marked这个默认渲染器的,由于我使用了hexo-renderer-markdown-it-plus这个渲染器,因此不行

也有方案是基于base64图片到网页里面,这样就无所谓路径问题了,这种方式我担心会网页加载过慢

最终我选用了Hexo + Typora + 开发Hexo插件 解决图片路径不一致提供的方案:

  • post_asset_folder将其设置为true,每次 hexo new page 生成新文章,都会在文章文件同级目录创建一个与文章文件名同名的文件夹,就在这里存放此文章的图片。

  • 然后就可以做这样的转换

    1
    ![example](postname/example.jpg) --> {% asset_img example.jpg example %}

从而符合https://hexo.io/zh-cn/docs/asset-folders这一篇官方文档用法

但是他的插件有bug,如果文章标题里面有特殊字符,js代码中的正则匹配会失效

根据Is there a RegExp.escape function in JavaScript?这一篇的建议

fork了项目修改代码

1
2
3
4
5
+function escapeRegex(string) {
+ return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
- var regExp = RegExp("!\\[(.*?)\\]\\(" + fileName + '/(.+?)\\)', "g");
+ var regExp = RegExp("!\\[(.*?)\\]\\(" + escapeRegex(fileName) + '/(.+?)\\)', "g");

从而解决问题

最后使用npm安装fork的项目

1
npm install https://github.com/tedcy/hexo-asset-img.git --save

加密博文

根目录下操作

  • npm install hexo-blog-encrypt

  • _config.yml文件中添加内容:

    1
    2
    encrypt:
    enable: true

使用插件

在想要使用加密功能的Blog头部加上对应文字:

1
2
3
4
5
6
7
8
9
---
title: Hexo加密功能
date: 2019-09-04 23:20:00
tags: [学习笔记,Hexo]
categories: Hexo
password: smile
abstract: Welcome to my blog, enter password to read. (可不填)
message: 密码输入框上描述性内容(可不填)
---

搜索功能

安装:npm install hexo-generator-searchdb --save

_config.yml中添加

1
2
3
4
5
6
search:
path: search.xml
field: post
format: html
limit: 10000
content: true
  • path

    文件路径。默认为 search.xml,如果将扩展名改为 .json,则输出格式为 JSON,否则使用 XML 格式。

  • field

    指定搜索范围。可选:

    • post(默认):只覆盖博客中已发布的所有文章;
    • page:只覆盖博客中所有页面;
    • all:覆盖博客中所有页面和已发布的文章。
  • content

    是否包含每一篇文章的内容。若为 false 则只会根据文章标题和 meta 元数据进行搜索,默认为 true

  • format

    页面内容的形式。可选:

    • html(默认):原始 html 字符串被缩小。
    • striptags:原始 html 字符串被缩小,并删除所有标签。
    • raw:每个帖子或页面的 Markdown 文本。

修改_config.next.yml

1
2
local_search:
enable: true

next更新

theme-next更新说明及常见问题

简单来说,问题就是 theme-next 团队的 owner - Ivan Nginx 始终拒绝向其它任何团队成员提供足够的权限,且 owner 本人自 2019 年 10 月起已连续半年不在线,导致其它活跃的团队成员无法管理仓库,也无法邀请新的成员。 由于对 theme-next 团队的未来不抱有期望,我作为 theme-next 的主要贡献者,自 2020 年 4 月起停止为旧的仓库贡献代码,并创建了新的组织,以确保维护工作正常进行。

更新到8.12.2

motion

动画效果感觉更慢了,暂时关闭了

mermaid

原生支持了mermaid的markdown语法

https://theme-next.js.org/docs/tag-plugins/mermaid.html

mermaid的themes里面,选了neutral,另外两个字有点细看不太清楚

调整图像大小

mermaid有个问题是节点过多的时候,显示的图太大了,此时可以使用官方文档配置官方文档useMaxWidth来调整

1
2
sequenceDiagram
%%{init: { 'sequence': {'useMaxWidth':true} } }%%
缩放和mermaid.live外链

next本身不支持,需要自己实现

根据gitlab的issue Add zoom and pan to mermaid diagrams,其中https://github.com/mermaid-js/mermaid/issues/2162#issuecomment-1542542439提供了一个从mermaid-live-editor借用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
<html>
<head>
<style type="text/css">
#mySvgId {
height: 90%;
width: 90%;
}
</style>
</head>
<body>
<div id="graphDiv"></div>
<script src="https://bumbu.me/svg-pan-zoom/dist/svg-pan-zoom.js"></script>
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: false });
// Example of using the render function
const drawDiagram = async function () {
const element = document.querySelector('#graphDiv');
const graphDefinition = `
flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`;
const { svg } = await mermaid.render('mySvgId', graphDefinition);
element.innerHTML = svg.replace(/[ ]*max-width:[ 0-9\.]*px;/i , '');
var panZoomTiger = svgPanZoom('#mySvgId', {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true
})
};
await drawDiagram();
</script>
</body>
</html>

直接清空next本身的mermaid代码

1
$ > themes/next/source/js/third-party/tags/mermaid.js

然后新增一个inject文件scripts/mermaid-injector.js,这个文件是在next的mermaid的基础上改的,根据demo新增了缩放功能

并且根据https://github.com/mermaid-js/mermaid-live-editor/issues/1290#issuecomment-1695446785提供的https://github.com/mermaid-js/mermaid-live-editor/blob/develop/src/lib/util/serde.ts代码实现了mermaid-alive的外链全屏展示

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
<script src="https://bumbu.me/svg-pan-zoom/dist/svg-pan-zoom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.11/pako.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.7.6/base64.min.js"></script>
<script>
// 当页面加载后执行的操作
document.addEventListener('page:loaded', () => {
// 查找所有存在".mermaid"类的元素
const mermaidElements = document.querySelectorAll('.mermaid');
if (mermaidElements.length) {
// 异步加载mermaid.js脚本
NexT.utils.getScript(CONFIG.mermaid.js, {
condition: window.mermaid
}).then(() => {
// pakoSerde用于将对象序列化成base64编码的字符串
// 或将一个base64编码的字符串解码并解压
const pakoSerde = {
serialize: (state) => {
const data = new TextEncoder().encode(state);
const compressed = pako.deflate(data, { level: 9 });
return Base64.fromUint8Array(compressed, true);
},
deserialize: (state) => {
const data = Base64.toUint8Array(state);
return pako.inflate(data, { to: 'string' });
}
};
// mermaidURI函数返回一个包含mermaid-alive外链的URL字符串
const mermaidURI = (str) => {
const state = {
"code": str,
"mermaid": '{"theme": "default"}',
"autoSync":false,
"updateDiagram":false,
"editorMode":"code",
"panZoom":true,
"zoom":1.3
};
return "https://mermaid.live/view#pako:" + pakoSerde.serialize(JSON.stringify(state));
}
// 初始化mermaid配置
mermaid.initialize({
startOnLoad : false,
theme : CONFIG.darkmode && window.matchMedia('(prefers-color-scheme: dark)').matches ? CONFIG.mermaid.theme.dark : CONFIG.mermaid.theme.light,
logLevel : 4,
flowchart: { curve: 'linear' },
gantt : { axisFormat: '%m/%d/%Y' },
sequence : { actorMargin: 50, 'showSequenceNumbers':true }
});
// 遍历mermaidElements,并对每个元素执行异步函数
mermaidElements.forEach(async (element, index) => {
const svgId = 'mySvgId' + index;
const drawDiagram = async function () {
// 渲染mermaid图表并获取SVG
const { svg } = await mermaid.render(svgId, element.innerText);

//修改height = 1000px
let svgNoConst = svg.replace('<svg ', '<svg height="1000px" ');
//不知道干啥的,demo复制过来的
svgNoConst = svgNoConst.replace(/[ ]*max-width:[ 0-9\.]*px;/i , '');

//创建一个新的div
const newElement = document.createElement('div');
newElement.innerHTML = svgNoConst;
newElement.className = element.className;

//svg后面追加一个换行
let br = document.createElement('br');
newElement.appendChild(br);

//再追加当前mermaid语法源码转化成的mermaid-alive外链
let a = document.createElement('a');
a.href = mermaidURI(element.innerText);
a.textContent = "View on mermaid.live";
a.target = "_blank";
newElement.appendChild(a);

//将父节点的子节点替换成新创建的子节点
//也就是原来的mermaid语法源码转化成了svg+br+超链
element.parentNode.replaceChild(newElement, element);

// 对SVG应用panZoom以允许用户缩放和平移图表
var panZoomTiger = svgPanZoom('#' + svgId, {
zoomEnabled: true,
controlIconsEnabled: true,
dblClickZoomEnabled: false,
fit: true,
center: true,
})
};
await drawDiagram();
});
});
}
});
</script>
更新mermaid版本

直接修改_vendors.yml文件,更新到想要的版本就行

1
2
3
4
5
6
7
8
9
--- a/themes/next/_vendors.yml
+++ b/themes/next/_vendors.yml
mermaid:
name: mermaid
- version: 9.1.3
+ version: 10.7.0
file: dist/mermaid.min.js
- integrity: sha256-TIYL00Rhw/8WaoUhYTLX9SKIEFdXxg+yMWSLVUbhiLg=
+ #integrity: sha256-TIYL00Rhw/8WaoUhYTLX9SKIEFdXxg+yMWSLVUbhiLg=

hexo-related-popular-posts插件已经不兼容hexo最新版本,换成hexo-related-posts

这个版本的相关文章推荐更科学了

解密显示TOC

默认情况下TOC解密后也不会显示

通过对根目录下themes/next/layout/_macro/sidebar.njk进行如下修改

1
2
3
4
5
6
7
8
9
10
11
  <aside class="sidebar">
{%- set display_toc = page.toc.enable and display_toc %}
{%- if display_toc %}
- {%- set toc = toc(page.content, {class: 'nav', list_number: page.toc.number, max_depth: page.toc.max_depth}) %}
+ {%- if (page.encrypt) %}
+ {%- set toc = toc(page.origin, {class: 'nav', list_number: page.toc.number, max_depth: page.toc.max_depth}) %}
+ {%- else %}
+ {%- set toc = toc(page.content, {class: 'nav', list_number: page.toc.number, max_depth: page.toc.max_depth}) %}
+ {%- endif %}
{%- set display_toc = toc.length > 1 and display_toc %}
{%- endif %}

参考资料

hexo官方文档

Hexo-Next 主题博客个性化配置超详细,超全面(两万字)

windows下使用hexo搭建博客

https://theme-next.js.org/