Menu

基于Jekyll搭建个人博客

post on 18 Jan 2019 about 9580words require 32min
CC BY 4.0 (除特别声明或转载文章外)
如果这篇文章帮助到你,可以请我喝一杯咖啡~

最开始我是在CSDN上写博客的。博客写了一年多,访问量勉强破万,感觉其中至少有三成是自己贡献的。而且 CSDN 上的广告是越来越多,网页编辑器也是越来越不好用。

十一月份,在青岛的 ICPC 区域赛失利之后,想要重新开始,和过去告别。也从此转移自己写博客的阵地。考虑过转投暂时还比较干净而且可以部分定义页面的博客园,但是最终还是决定在Github Pages上搭建属于自己的博客。

然后,翻了一些网上的教程,用Hexo框架和NexT主题搭了一个比较素雅的符合个人审美的博客。然而,Hexo 搭建的博客需要先在本地生成博客页面然后再上传到服务器上,每次发博客都需要在电脑重新生成页面并上传,感觉没有那么方便。在不同电脑上想要发博客更是十分困难,感觉如果胡乱操作后本地部署的文件发生了一定的错乱,恢复起来会十分麻烦。

后来发现,Github Pages 原生支持Jekyll,因此如果只需要推送文章可以直接在 github 的仓库里做。于是决定在期末考试后用 Jekyll 重新搭建个人博客。原来的博客不再维护。

构思

目前博客的 demo 如下:

demo

想搭建一个足够干净、页面几乎不要有多余元素的博客,同时博客的功能还要足够丰富,满足我以下的需求:

  • 用 Markdown 写博客
  • 支持 Latex 公式
  • 支持 mermaid 等插件
  • 个性化 Live2D
  • 分享一些我在其他网站的个人账号
  • 移动端适配
  • 足够快的加载速度
  • 访问量统计,评论互动
  • 需求是会逐渐增长的!

其中,Live2D 是我某次访问别人的博客初次见到的,感觉非常吸引人。最终的目标是去掉网页上所有多余的元素,只剩下博文和 Live2D 看板娘,看板娘本身还可以作为博客访问的导航,感觉会十分有新意。

搭建过程(不定期更新)

我目前读的专业是计科超算方向,所以软件工程专业相关方面的基础几乎为 0😌,大部分的内容都是参考网上教程慢慢摸索的,假如对某些过程有意见和建议欢迎向我提出!

开发环境

我的博客前端后端分离,本地只需要下载后端博文文件即可,前端部分使用 jekyll-remote-theme 远程引用。这个学期选了一门 Linux 相关的公选课,感觉还不错,因此直接把开发环境是搭建在 Windows Subsystem for Linux(简称 WSL)上了,Windows 下也可以直接访问其中的环境,详见这篇博客

1
2
3
4
5
6
sudo apt install -y ruby ruby-dev make gcc g++ zlib1g-dev
# gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
sudo gem install github-pages
git clone --depth=1 https://github.com/wu-kan/wu-kan.github.io
cd wu-kan.github.io
github-pages build --destination _site

jekyll 博客是基于 ruby 语言开发的,要先将 ruby 装进来。此外,国内访问一些资源可能会很慢,推荐去掉上面的注释,使用国内镜像。

github 上的 jekyll 和普通的 jekyll 是有一点点区别的,比如说只支持特定的插件。我希望与 github 保持兼容,因此配置的依赖是直接引入了 github 版本的 jekyll,安装时间略久。

我在一个全新的 Linux 中安装了一遍,需要的依赖已经在上面的「开发环境中列出来的」。如果bundle install在你的机器上还是报错,要看报错信息,缺啥装啥。

1
jekyll s

由于我的博客已经达到了三百篇以上,在本地生成网页的速度有些慢,在我的机器上要跑三分钟(关掉配置项中的侧边栏总字数统计就快了,可达 25s)。

1
2
    Server address: http://127.0.0.1:4000/
  Server running... press ctrl-c to stop.

看到如上内容时就成功了,本地访问http://127.0.0.1:4000/即可查看效果。

定制

我把博客实现的的插件基本上都封装起来了,可以直接在浏览器按 F12 查看我是怎么引入的。如果你想增加新的渲染规则,可以新建_layouts目录,将你自己的渲染规则放进去,见目录结构 -Jekyll

$\KaTeX$

参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
<link
  rel="stylesheet"
  href="//cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css"
/>
<script
  src="//cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js"
  defer="defer"
></script>
<script
  src="//cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js"
  defer="defer"
  onload='renderMathInElement(document.body, { delimiters: [{left: "$$", right: "$$", display: true}, { left: "$", right: "$", display: false }, {left: "\\(", right: "\\)", display: false}, {left: "\\[", right: "\\]", display: true}]})'
></script>

不过这样仅能显示 $E=mc^2$ 这样的行间公式,行内公式是没有办法显示的(issue)。

1
2
3
$$
E=mc^2
$$

一种方法是引入 mathjax 进行渲染,但是这样做就失去了用 katex 的初心了。后来我发现 katex 有对应的插件可以解决这一问题。在上面代码后补充如下代码:

1
2
3
4
<script
  src="//cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/mathtex-script-type.min.js"
  defer="defer"
></script>

效果如下。

\[E=mc^2\]

可以在这个页面查看一些别的插件,比如化学方程式的插件等等,都是可以引入的。

mermaid 图

我希望自己能按照 markdown 代码块扩展的规则来写 mermaid 流程图:

1
2
3
4
5
6
7
8
9
10
11
```mermaid
sequenceDiagram
Alice->>John: Hello John, how are you?
loop Healthcheck
    John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
```

其效果如下。

sequenceDiagram
Alice->>John: Hello John, how are you?
loop Healthcheck
    John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!

因此需要插入如下内容(将代码块隐藏,并在原位置插入 mermaid 图):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<style>
  pre code.language-mermaid {
    display: none;
  }
</style>
<script
  src="https://cdn.jsdelivr.net/npm/mermaid@8.7.0/dist/mermaid.min.js"
  defer="defer"
  onload='
    for(let x of document.getElementsByClassName("language-mermaid"))
      if(x.nodeName=="CODE")
      {
        let m = document.createElement("div");
        m.classList.add("mermaid");
        m.textContent = x.textContent;
        x.parentNode.insertAdjacentElement("beforebegin", m);
      }'
></script>

vega-lite 图

可以交互的统计图表,见 https://vega.github.io/vega-lite/examples/selection_layer_bar_month.html

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
```vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {"url": "https://vega.github.io/vega-lite/data/seattle-weather.csv"},
  "layer": [{
    "params": [{
      "name": "brush",
      "select": {"type": "interval", "encodings": ["x"]}
    }],
    "mark": "bar",
    "encoding": {
      "x": {
        "timeUnit": "month",
        "field": "date",
        "type": "ordinal"
      },
      "y": {
        "aggregate": "mean",
        "field": "precipitation",
        "type": "quantitative"
      },
      "color": {
        "timeUnit": "month",
        "field": "date",
        "type": "nominal"
      },
      "opacity": {
        "condition": {
          "param": "brush", "value": 1
        },
        "value": 0.7
      }
    }
  }, {
    "transform": [{
      "filter": {"param": "brush"}
    }],
    "mark": "rule",
    "encoding": {
      "y": {
        "aggregate": "mean",
        "field": "precipitation",
        "type": "quantitative"
      },
      "color": {"value": "firebrick"},
      "size": {"value": 3}
    }
  }]
}
```

显示效果如下(选中对应区域可以计算对应平均值)。

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {"url": "https://vega.github.io/vega-lite/data/seattle-weather.csv"},
  "layer": [{
    "params": [{
      "name": "brush",
      "select": {"type": "interval", "encodings": ["x"]}
    }],
    "mark": "bar",
    "encoding": {
      "x": {
        "timeUnit": "month",
        "field": "date",
        "type": "ordinal"
      },
      "y": {
        "aggregate": "mean",
        "field": "precipitation",
        "type": "quantitative"
      },
      "color": {
        "timeUnit": "month",
        "field": "date",
        "type": "nominal"
      },
      "opacity": {
        "condition": {
          "param": "brush", "value": 1
        },
        "value": 0.7
      }
    }
  }, {
    "transform": [{
      "filter": {"param": "brush"}
    }],
    "mark": "rule",
    "encoding": {
      "y": {
        "aggregate": "mean",
        "field": "precipitation",
        "type": "quantitative"
      },
      "color": {"value": "firebrick"},
      "size": {"value": 3}
    }
  }]
}

因此需要插入如下内容(将代码块隐藏,并在原位置插入 vega-lite 图):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
  pre code.language-vega-lite {
    display: none;
  }
</style>
<script
  src="https://cdn.jsdelivr.net/combine/npm/vega@5.31.0,npm/vega-lite@5.23.0,npm/vega-embed@6.29.0"
  async="async"
  onload='
    for(let x of document.getElementsByClassName("language-vega-lite"))
      if(x.nodeName=="CODE")
      {
        let m = document.createElement("div");
        x.parentNode.insertAdjacentElement("beforebegin", m);
        vegaEmbed(m, JSON.parse(x.textContent));
      }'
></script>

评论系统

配置详见https://giscus.app/。请使用你自己的 repo!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script src="https://giscus.app/client.js"
  data-repo="wu-kan/wu-kan.github.io"
  data-repo-id="MDEwOlJlcG9zaXRvcnkxNzIxMzc1OTQ="
  data-category="Announcements"
  data-category-id="DIC_kwDOCkKces4Clz9j"
  data-mapping="url"
  data-strict="0"
  data-reactions-enabled="1"
  data-emit-metadata="0"
  data-input-position="top"
  data-theme="preferred_color_scheme"
  data-lang="en"
  crossorigin="anonymous"
  async>
</script>

加入看板娘

摇摇乐

dsrkafuu/sakana-widget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/sakana-widget@2.7.0/lib/sakana.min.css"
/>
<script>
function initSakanaWidget() {
  const mizuno = SakanaWidget.getCharacter('takina');
  mizuno.image = `https://mizuno-ai.wu-kan.cn/pixiv/75805052_p0.webp`;
  SakanaWidget.registerCharacter('mizuno', mizuno);
  let sakana = document.createElement("div");
  sakana.style.bottom = "0";
  sakana.style.right = "0";
  sakana.style.zIndex = "1";
  sakana.style.position = "fixed";
  document.body.insertAdjacentElement("beforeend", sakana);
  new SakanaWidget({ character: 'mizuno' }).mount(sakana);
}
</script>
<script
async
onload="initSakanaWidget()"
src="https://cdn.jsdelivr.net/npm/sakana-widget@2.7.0/lib/sakana.min.js"
></script>
Live2D(以前使用)

参考了:

由于 live2d 的协议是 GPL,这里我不把它的源码拷贝到自己的博客项目了,直接用链接引入。在 html 的任意位置,或是我博客对应的append选项下插入下述 html 代码:

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
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/gh/Dreamer-Paul/Pio@2.4/static/pio.min.css"
/>
<script
  defer="defer"
  src="https://cdn.jsdelivr.net/gh/Dreamer-Paul/Pio@2.4/static/l2d.min.js"
></script>
<script
  defer="defer"
  src="https://cdn.jsdelivr.net/gh/Dreamer-Paul/Pio@2.4/static/pio.min.js"
  onload='
    if (screen.width > screen.height)
    {
      let pio_container = document.createElement("div");
      pio_container.classList.add("pio-container");
      pio_container.classList.add("right");
      document.body.insertAdjacentElement("beforeend", pio_container);
      let pio_action = document.createElement("div");
      pio_action.classList.add("pio-action");
      pio_container.insertAdjacentElement("beforeend", pio_action);
      let pio_canvas = document.createElement("canvas");
      pio_canvas.id = "pio";
      pio_canvas.style.width = "14rem";
      pio_canvas.width = "600";
      pio_canvas.height = "800";
      pio_container.insertAdjacentElement("beforeend", pio_canvas);
      let pio = new Paul_Pio({
        "mode": "draggable",
        "hidden": true,
        "content": {
          "skin": ["要换成我的朋友吗?", "让她放个假吧~"],
          "custom": [{
            "selector": "a",
            "type": "link",
          }, {
            "selector": ".sidebar-toggle",
            "text": "打开侧边栏叭~"
          }, {
            "selector": ".effect-info",
            "text": "哇,你发现了什么!"
          }, {
            "selector": "#sidebar-search-input",
            "text": "想搜索什么呢?很多干货哦!"
          }, {
            "selector": "#toc",
            "text": "这是目录~"
          }, {
            "selector": ".page-title",
            "text": "这是标题~"
          }, {
            "selector": ".v",
            "text": "评论没有审核,要对自己的发言负责哦~"
          }]
        },
        "model": [
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/33/model.2018.bls-winter.json",
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/platelet-2/model.json",
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/xiaomai/xiaomai.model.json",
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/mashiro/seifuku.model.json",
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/seele/model.json",
          "https:\/\/cdn.jsdelivr.net/gh/imuncle/live2d/model/Violet/14.json",
          "https:\/\/cdn.jsdelivr.net/gh/xiaoski/live2d_models_collection/Kobayaxi/Kobayaxi.model.json",
          "https:\/\/cdn.jsdelivr.net/gh/xiaoski/live2d_models_collection/mikoto/mikoto.model.json",
          "https:\/\/cdn.jsdelivr.net/gh/xiaoski/live2d_models_collection/uiharu/uiharu.model.json"]
      });
    }'
></script>

自定义参数见Dreamer-Paul/Pio,模型收集自imuncle/live2dxiaoski/live2d_models_collection,。

下一步计划

详细的开发计划及建站内容详见主题详情页

这个页面得到的灵感,目标是博客上除了文章和作为导航的 Live2D 之外不出现其他的模块,尽量的干净、快速。

参考资料

大部分的参考链接已在原文给出,同时搭建本博客的过程中还参考了以下内容,感谢作者们:

Related posts

Loading comments...