摸鱼

关于博客的碎碎念

部署、优化以及乱七八糟的事

Renovamen
2020-10-07
11 min

“虽然研究进度堪忧,但鱼还是要摸的”,在这样的理念的驱动下,菜鸡最终折腾出了一个目前看上去还算可以的方案。

我的博客最初 fork 自 Huxpro/huxpro.github.io,用了一段时间之后开始瞎改,把别人干净的代码改得乱七八糟。博客用的是 Jekyll 框架,而 Jekyll 就是 Github Pages 的默认引擎,所以在部署的时候 Github Pages 连 build 这一步都帮你省了。于是在很长一段时间内,作为一只懒惰的菜鸡,我并没有什么动力来折腾这些东西。而现在之所以要折腾,是因为不折腾的确不行了。

WARNING

不过这版博客已经是用 VuePress 重写过后的版本了,所以这篇文章中的某些部分已经并不适用了...

# 奇怪的起因

说起来这还是一个漫长的故事。首先我的博客的公式渲染引擎用的是 Katex,因为它快,比 Mathjax 快多了(可以参考这里)。

kramdown 的默认数学公式渲染引擎是 Mathjax,而从 v2.0.0 开始 kramdown 似乎引入了一些插件来支持 Katex,比如 kramdown-math-katex。那么问题来了,Github Pages 的 Jekyll 是不支持除了这些插件以外的插件的。那么唯一的办法就只有在本地 build 网站,然后把 build 好的文件 push 到 gh-pages 分支上去。

不行,这样多麻烦,Jekyll 的天生优势不就没了吗,不然我为啥不用 Hexo 或者 Hugo,它们还比 Jekyll 快,不行不行。

那个时候还没有 Github Actions 这种东西,我又完全不知道别的持续集成方案,就算知道了估计也不想去折腾。于是我在这里看到了一个比较 hack 的方法,首先让 kramdown 以为我们要用 Mathjax:



 

markdown: kramdown    
kramdown:
  math_engine: mathjax

这样 kramdown 就会把像 $$a + b$$ 这样的公式转换成 Mathjax 能识别的 HTML 形式:

<script type="math/tex">a + b</script>

然后通过 JavaScript 把 <script type="math/tex"> 标签里的东西用 Katex 渲染出来:

$("script[type='math/tex']").replaceWith(function() {
  var tex = $(this).text();
  return katex.renderToString(tex, {
    displayMode: false
  });
});

$("script[type='math/tex; mode=display']").replaceWith(function() {
  var tex = $(this).html();
  return katex.renderToString(tex.replace(/%.*/g, ''), {
    displayMode: true
  });
});

虽然看上去不太优雅,但好歹能用,本菜鸡的理念是能用就行,于是就这样过了一段时间。直到几个月前,我发现我博客的公式不对劲了,它们突然都变成了这样:\(a + b\)

我一脸懵逼,第一反应是我是不是又把博客哪儿的代码搞崩了,但想了想我似乎也没有动过公式那部分的代码?而且当时我部署在 Coding Pages 上的博客公式还是正常的,那么大概率就是 Github Pages 哪里不对劲了。

搜了一下发现 Github Pages 把 kramdown 的版本更新到了 v2.2.0,从这一版开始,为了兼容 Mathjax v3.x,kramdown 会把 inline math 转换成 \(a + b\) 的形式,把 block math 转换成 \[a + b\] 的形式(参考这个 commit),于是上面那段脚本就坏掉了。

于是一切都回到了最初的起点,摆在面前的办法只剩下把在本地用 kramdown v2.1.0 build 好的网站扔 gh-pages 上去。这样干了几个月后懒惰的我实在忍不了了,并决定让 Github Actions 来帮我干这件事。

所以我当初为啥不选 Hexo...

# Github Actions

在 repo 下建一个目录 .github/workflow,在这个目录下放一个 .yaml 格式的 workflow 文件。GitHub 只要发现 .github/workflows 下有 .yaml 文件,就会自动运行它们。这里是 workflow 文件的详细文档。

# Jekyll

Jekyll 官方已经给了一个现成的 action,直接引用它就好:

name: build Jekyll site and deploy it to GitHub Pages

# 检测 master 分支上的推送和 pr
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  jekyll-build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v2
      
      # 检测 vendor/bundle 下有没有已经安装好的包
      # 如果有的话就不用再 bundle install 了,节省时间和资源
      - name: check cache
        uses: actions/cache@v1
        with:
          path: vendor/bundle
          key: runner.os−gems−{%raw%}{{ hashFiles('**/Gemfile.lock') }}{% endraw %}
          restore-keys: |
            ${{ runner.os }}-gems-
      
      # 引用 helaili/jekyll-action 来打包 Jekyll 网站
      # 并把打包好的文件推到同一个 repo 的 gh-pages 分支
      - name: build and deploy
        uses: helaili/jekyll-action@2.0.4
        env:
          JEKYLL_PAT: ${{ secrets.GITHUB_TOKEN }}
        with:
          target_branch: 'gh-pages'

相当于这个 workflow 会自动检测 master 分支上的 push 和 pull_request,一旦检测就到准备环境,然后运行 bundle exec jekyll build 打包网站,并把打包产物扔 gh-pages 分支上去。

需要注意的是必须得有一个 GemfileGemfile.lock 文件,Gemfile 里面写上要装的包,比如本博客的 Gemfile 长这样:

source 'https://rubygems.org'

gem 'jekyll', '~> 4.0'
gem 'kramdown', '= 2.1.0'  # 这里为了 Katex 把 kramdown 版本固定在了 2.1.0
gem 'jemoji', '~> 0.11.1'
gem 'jekyll-paginate', '~> 1.1.0'

如果用了自定义域名,那就还需要在 master 分支放一个 CNAME,这样 helaili/jekyll-action 就会把 CNAME 也推到 gh-pages 分支去。直接在 gh-pages 分支加 CNAME 是没有什么用的,因为下次自动推送时它就会被清掉...

# Node.js

所有能用 Node.js 搞定的东西(比如这个基于 VuePress 的博客)都能用以下工作流处理:

# npm

name: build and deploy

# 检测 master 分支上的推送和 pr
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build-and-deploy-vuepress:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      # Node.js 环境
      - name: Setup Node
        uses: actions/setup-node@v2.1.2
        with:
          node-version: '12.x'

      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      
      # npm run build
      - name: Build
        run: |
          npm ci
          npm run build
      
      # 推送到同一个 repo 的 gh-pages 分支
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: dist # build 输出文件夹
          cname: zxh.io  # 如果用了自定义域名,在这里设置

其中 peaceiris/actions-gh-pages 也是一个别人写好的 action,能把指定路径的文件推到 gh-pages 分支。

# yarn

如果包管理工具用的 yarn

name: build and deploy

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build-and-deploy-vuepress:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2.1.2
        with:
          node-version: '12'

      - name: Get yarn cache
        id: yarn-cache
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ${{ steps.yarn-cache.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-
      
      - name: Build
        run: |
          yarn install --frozen-lockfile
          yarn build
      
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: dist
          cname: zxh.io

# 优化

# 减少依赖

通过砍掉一些觉得用不太上的功能,和直接手写一些比较简单的功能,去掉了一些依赖。但我已经忘了去掉了哪些了...

# 合并依赖

为了减少请求次数,可以尽量把多个 JS 或 CSS 文件合并压缩成一个。上 Gulp 之类的工具当然也可以,不过 jsDelivr 自带了合并功能,比如要合并以下两个库:

只需要:

https://cdn.jsdelivr.net/combine/npm/chart.js@2.9.3/dist/Chart.min.js,npm/mermaid@8.4.8/dist/mermaid.min.js

于是我把除了 Katex 和 Mathjax(因为这俩涉及到引用字体的问题)以外的文件都合并成了一个 lib.min.js

# CDN

又一次喜闻乐见的白嫖 jsDelivr,对于托管在 Github 上的博客来说,在静态资源路径前加上以下 URL 即可:

https://cdn.jsdelivr.net/gh/{{ 用户名 }}/{{ 仓库名 }}@{{ 分支名 }}

比如:

https://cdn.jsdelivr.net/gh/Renovamen/renovamen.github.io@master/js/lib.min.js

# Vercel

我一直以来都在听说双线部署甚至多线部署这种提高访问速度的操作,但一直没有去折腾。因为 Github Pages 虽然境内访问慢了点,但至少还是能访问的,那么能用就行了。直到几个月前,我发现 Github Pages 境内完全访问不上了...

间歇性抽风想想还是很让人不安,那行吧行吧双线部署我来了...

那时候我所知的有境内节点的静态网站托管服务就 Gitee PagesCoding Pages 俩,而 Gitee Pages 不能免费自定义域名,于是就把境内的线路解析到了 Coding Pages 上。

然而不久之前,新版 Coding Pages 的静态网站合并到腾讯云静态网站,并开始收费了。之前旧版的静态网站还能正常部署和访问,但之后的网站就都得用新版。收费倒是并不贵且也没有什么问题,问题在于如果域名不备案的话,即使是境内的访问,Coding 也会强制给你上境外 CDN 加速,于是速度会很慢,估计比直接访问 Github Pages 还慢。

于是就手忙脚乱的把境内线路的博客扔到了 Vercel 上。只能说 Vercel 至少到目前为止还没发现什么毛病,有境内节点(不过感觉还是没有以前的 Coding Pages 快?),而且是直接自动从 Github 仓库上拉代码然后打包加部署,对菜鸡相当友好。

以前为啥就没发现呢。