Render HTML Statically

这个“静态”网站逐渐添加了越来越多的渲染脚本 (KaTeX\KaTeX, Highlight.js, Mermaid.js, 等)。它们极大地拖慢了渲染速度。

由于这些渲染结果都是一致的,应当移到编译期完成。让静态的归静态。

最开始,我研究了一下搭配 jsdom 和 jquery 后,Highlight.js, Mermaid.js 在 Node.js 上的表现。我想看看是不是能执行一个非常简单的 Node.js 脚本就能达到我的需求。

花费了一两个小时后,我放弃了。基本上它们很难在 Node.js 上如在浏览器里一样可用。

最后的方案还是变成了 Puppeteer。将页面灌到 Chrome 里,然后拿到渲染完毕的 HTML,然后删掉那些渲染脚本。


首先给渲染脚本的 tag 添加 data-stage="prerender" 属性,以便于后续将它们从 HTML 中删除。

<script data-stage="prerender"> renderMathInElement(document.body); </script>

然后通过 Post Renderer 遍历整个生成的 public 目录,忽略掉那些 size 小于 1000 的文件(一般是 Hugo 生成的重定向 HTML),然后将它们一个个灌入 Puppeteer。

得到生成的 HTML 后,通过 jsdom 和 jquery 把 data-stage="prerender" 的元素全部删除。

这样就得到了完全渲染好的页面,给 Actions 添加一个新的部署流程,就完成了。目前本站已经应用了这个预渲染方式。

- name: Setup Nodejs
  uses: actions/setup-node@v2
  with:
    node-version: '12'
- name: Pre Render Pages
  run: mkdir rendered && cd post-render && npm install && node ./render.js .. ../rendered

- name: Deploy Pre Rendered
  uses: peaceiris/actions-gh-pages@v3
  with:
    publish_branch: rendered
    personal_token: ${{ secrets.PERSONAL_TOKEN }}
    publish_dir: ./rendered
    external_repository: lingsamuel/lingsamuel.github.io

这样即使在禁用了 JS 的环境中,也可以获得完整的阅读体验。同时也避免了页面载入后花费几秒钟渲染,然后元素一直在抽搐的问题。这很文明。

可以通过在 DevTools 中,通过 Ctrl + Shift + P 搜索 Disable Javascript 来测试一下,例如这个页面


目前的预渲染脚本:


虽然还存在一些问题,例如如果 js/css 文件使用相对路径的话,Puppeteer 会从本地寻找;使用绝对路径的话,会去寻找线上版本,而使得运行的 js/css 实际上低了一个版本。

由于实际上我只有一个自定义 js,这个问题还不是非常严重,我懒得折腾了。