博客历史演进

TL;DR

主要框架

附属模块

演进记录

Hexo 时期

  • 主题太多,选择太多,反而迷失了自我
  • 本身就讨厌 npm 和 node_modules,每次编译很烦,懒得折腾

VuePress 时期

  • 配置项太多了,非常麻烦
  • 同 hexo,用 npm 编译,看到 node_modules 就害怕
  • 编译产物比 hexo 还大

Mdbook 时期

动机

  • 左找右找,正好当时在学 Rust,看到 Mdbook 不错
  • 看了一下官方文档,配置项也特别少,适合自己这种懒人,就改为 mdbook 了
  • 刚开始是出于懒选择的 mdbook,后面发现自己做的工作其实不比其他静态网站生成框架少

困难

  • 切换成 mdbook 本身没什么困难,所有困难主要集中在附属模块上
  • mdbook 本身的困难只有一次,由于 mdbook 版本升级带来的 index.hbs 带来的预置代码变动
  • 详见这篇博客和本博客 Github 上源码关于 index.hbs 带来的更改,另外,Rust 语言圣经也遇到了这个问题,其更改也可以参考

添加附属模块

TOC

  • 切换到 mdbook 首先问题就是没有文章目录
    • 习惯了先浏览文章大纲,建立框架再补充文章细节,很不习惯
  • 浏览了网上所有关于 mdbook 的 toc 方法,都不太满意
  • 大多数是在每篇文章顶部添加 toc 链接
    • 这会占据文章的内容空间,不仅不优雅,每次想跳到另一个章节的时候,还要滚回文章顶部,非常麻烦
  • 正巧当时在学 rust,Rust 语言圣经的 mdbook 里就有 toc 侧边栏,很好奇其实现,就去翻源码
    • 源码在 assets/custom.js,里面的代码大部分都是自定义,toc 侧边栏实现大概是遍历网页的 tagName
    • 还有一些其他附加功能,比如当前页面停留三十秒以上,视为已读过,mdbook 的侧边栏,该文章标题会变灰作为提示
  • 照着实现 toc 和研究 hbs 代码花了很多时间(因为当时不懂 hbs,其实现在也不懂
    • 途中还遇到了一个怎么都解决不了的难题,最后发现是文件名为 head.hbs,mdbook 会视该文件仅为页面的 head 部分,改成 index.hbs 就好了

评论

  • 评论模块也是参照 Rust 语言圣经,使用 giscus 实现,不过直到现在都没有人用(挺正常的)
    • 评论模块没有太大的问题
    • 之前还附带有 view counter,但是负责 count 的那个网站似乎被炸了,无法完成功能,所以也就把相关代码删了

最近更新

  • mdbook 不按照时间进行倒序排列,而是按照书一样分章节组织
  • 所以改动了哪里也不知道,对于读者来说可能有点烦,每次都要跑到 github 上看代码提交
  • 脚本花了一段时间,也犯了比较多的错,直到现在这个脚本都有 bug,而且不通用,不过也懒得改了
    • 每次出 bug 的时候,比如 sed 找不到代表代码块的 ``` 标志,就会直接把 last-updated.md 全删了,再 add && commit,主打一个懒

站内搜索

  • mdbook 的中文搜索和 telegram 的中文搜索一样烂,自己写了太多东西记不到,只记得关键词,搜不到就很烦
    • mdbook 的搜索,根据西方文字自身的空格作为分词符,但中文根本不用空格这种东西,就没法分词
  • 机缘巧合找到了 docsearch,申请了一下,通过了,官方也给了教程怎么搭建
    • 不过 docsearch 很长一段时间我都没用到,因为搜索框死活出不来
    • 本来人就懒,就一直在摆烂,后面有一次有时间了,也实在忍不了搜索之殇了,就好好排查了一下问题
    • 结果发现是 index.hbs 里面,关于 docsearch 的配置少了一个逗号,排查出来自己都想笑,怎么犯了这么低级的错误
  • docsearch 是 algolia 的子服务,要先注册 algolia,然后一步一步配置
    • 甚至要自己配置 algolia 的 crawler(爬虫),自定义爬取的内容,才能加到 algolia 的倒排索引里面,也才能搜索到
    • 本来就懒,简直逆了天了,照着网上稍微配置了一下,自定义了一下就没管了
    • 有一些配置上的 bug 到现在都还没修……
  • docsearch 用起来倒是挺好用
  • 配置在附录,如果有需要可以参考一下

收录网站

  • 后来想把网站加到搜索引擎里,想着可能会帮到别人
    • 一进 Google 和 Bing 的站长工具,又要在 html 的 head 里面加东西,很麻烦
    • 加好了之后,又说需要站点地图才能爬网,当时连站点地图都不知道是啥
  • 随便搜了一个生成站点地图的工具 static-sitemap-cli,还好用起来不复杂
    • 可惜又是依赖 node,用 js 写的工具……
    • 每次本地编译完了之后,就会用这个工具生成站点地图,都在脚本里,自动化一下就可以了
  • 注意,随着文章越来越多,站点地图 sitemap.xmlsitemap.txt 会越来越大,而且 xml 文件每次编译里面的时间都会变,所以 last-updated.md 会越拖越长,这点在写脚本的时候尤其要注意

部署

通过分支部署

  • mdbook 博客搭建好的时候,github pages 的“通过 github actions 部署”还是 experimental(实验性的),采取了主流方案:Deploy from branch(通过分支部署)
    • 但是当时也写了一个 github actions 每次推送的时候自动编译,然后推送到 gh-pages 分支上,就不用自己本地编译了
    • 本地编译一遍主要是因为站点地图和生成 last-updated.md 需要,如果不做这两个工作可以直接推送到 remote
  • 当时用的别人的 actions,自己也没注意到 git 会 tracing 编译后的产物,这些产物都在 gh-pages 里,导致仓库越来越大,仅 .git 文件夹就有 800 多 MB,加上编译产物就有 1G 之大,被迫瘦身

通过 Github Actions 部署

  • 原计划是清理 gh-pages 分支,删除所有 git history
    • 也确实这么做了,从 834MB 减到了 26.8MB,详见这篇博客
    • 但是发现这个时候“通过 Github Actions 部署”已经不再是实验性的了
    • 经过提醒,于是从 Deploy from branch 改为 Github Actions,所以 gh-pages 分支也就用不到了,直接删除
  • 参照 mdbook wiki 里的官方部署教程,自己重写了 Github Actions 文件

使用 Cloudflare Pages

  • 这个想法是源于,经常给国内没法翻墙的朋友发博客内容很不方便,半天转不出来,想方便一下国内的朋友
    • 找了很多静态网站托管服务,要么被墙要么要钱,很搞人,自己甚至还开了一个月腾讯云的静态网站托管服务,结果域名要备案,还要上传 ssl 证书,纯纯大冤种
    • gitea + drone CI/CD,腾讯云,vercel,netlify,Gitee Pages 都试过了,没啥用
      • 最哈皮的还是 Gitee,这玩意儿居然会从源代码里面审查违规内容
      • 而且规则不透明,上一次部署能通过的文章,下一次部署就通过不了了
      • 甚至林清玄的文章都是违规内容
      • 也不告诉你哪儿错了,就告诉你这篇文章有问题,叫人自己排查
      • 开源中国的反馈 repository,里面至少一半多的 issue 都是反应自己无法部署
  • Cloudflare Pages 刚开始用了一下,但是没有 mdbook 的选项,也就不了了之,结果后面搜出来有一种比较 dirty 的做法可以曲线救国
    • Cloudflare Pages 直接导入 Github 仓库
      • 构建配置的框架预设选
      • 构建命令填 curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.37/mdbook-v0.4.37-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build
      • 构建输出目录填 book
    • 本解决方案参考这篇同样使用 mdbook 作为博客的博文
      • Cloudflare Pages 官方说支持 mdbook 构建,但实际上构建选项里并没有 mdbook
      • 有人在对此的 issue 里提出了一个比较 dirty 的做法,还真有用……
      • 为什么说 dirty,因为这个方法本质上是在 Cloudflare Pages 的构建服务器上,暴力拉 mdbook 的 binary 下来,构建命令当成注入到别人服务器里的 hack 命令用,看一下构建 log 就知道了(构建日志详见附录)
        • 重点在 00:14:26.223 那一行,构建命令实际上是一个 user command,按理来说可以乱填,这里拉 mdbook binary 下来就是不太合规的做法
      • 不过能用就行……
      • Cloudflare Pages 在国内的访问速度其实一般,不过好歹能访问,不像 Github Pages
  • 感谢一只昀喵辛勤的指导,受教了

附录

Algolia Crawer 配置

new Crawler({
  rateLimit: 8,
  maxDepth: 10,
  maxUrls: 5000,
  startUrls: ["https://tinysnow.github.io"],
  renderJavaScript: false,
  sitemaps: [],
  ignoreCanonicalTo: false,
  discoveryPatterns: ["https://tinysnow.github.io/**"],
  schedule: "at 13:14 on Tuesday",
  actions: [
    {
      indexName: "tinysnowio",
      pathsToMatch: ["https://tinysnow.github.io/**"],
      recordExtractor: ({ $, helpers }) => {
        // 去除不想要的 DOM
        $("#sidebar").remove();
        $("#menu-bar").remove();
        $("#menu-bar").remove();

        const records = helpers.docsearch({
          recordProps: {
            lvl0: {
              selectors: "",
              defaultValue: "博客",
            },
            lvl1: ["h1"],
            lvl2: ["h2"],
            lvl3: ["h3"],
            lvl4: ["h4"],
            lvl5: ["h5"],
            lvl6: ["h6"],
            content: [
              "p",
              "li",
              "ul",
              "ol",
              "code",
              "span",
              "strong",
              "a",
              "em",
            ],
          },
          aggregateContent: true,
          recordVersion: "v3",
        });
        // 添加语言
        records.forEach((record) => {
          record.lang = "zh-CN";
        });
        return records;
      },
    },
  ],
  safetyChecks: { beforeIndexPublishing: { maxLostRecordsPercentage: 100 } },
  initialIndexSettings: {
    tinysnowio: {
      attributesForFaceting: ["type", "lang"],
      attributesToRetrieve: [
        "hierarchy",
        "content",
        "anchor",
        "url",
        "url_without_anchor",
        "type",
      ],
      attributesToHighlight: ["hierarchy", "content"],
      attributesToSnippet: ["content:10"],
      camelCaseAttributes: ["hierarchy", "content"],
      searchableAttributes: [
        "unordered(hierarchy.lvl0)",
        "unordered(hierarchy.lvl1)",
        "unordered(hierarchy.lvl2)",
        "unordered(hierarchy.lvl3)",
        "unordered(hierarchy.lvl4)",
        "unordered(hierarchy.lvl5)",
        "unordered(hierarchy.lvl6)",
        "content",
      ],
      distinct: true,
      attributeForDistinct: "url",
      customRanking: [
        "desc(weight.pageRank)",
        "desc(weight.level)",
        "asc(weight.position)",
      ],
      ranking: [
        "words",
        "filters",
        "typo",
        "attribute",
        "proximity",
        "exact",
        "custom",
      ],
      highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">',
      highlightPostTag: "</span>",
      minWordSizefor1Typo: 3,
      minWordSizefor2Typos: 7,
      allowTyposOnNumericTokens: false,
      minProximity: 1,
      ignorePlurals: true,
      advancedSyntax: true,
      attributeCriteriaComputedByMinProximity: true,
      removeWordsIfNoResults: "allOptional",
    },
  },
  appId: "GX9RTL51BH",
  apiKey: "PlaceHolder",
});

Cloudflare Pages 构建日志

00:14:23.159	Cloning repository...
00:14:24.014	From https://github.com/TinySnow/TinySnow.github.io
00:14:24.015	 * branch            3685b9c14d244ab2a2888d820486e261e5174aea -> FETCH_HEAD
00:14:24.015	
00:14:24.139	HEAD is now at 3685b9c Added some Pharmacology content
00:14:24.139	
00:14:24.222	
00:14:24.222	Using v2 root directory strategy
00:14:24.250	Success: Finished cloning repository files
00:14:25.960	Checking for configuration in a wrangler.toml configuration file (BETA)
00:14:25.960	
00:14:26.071	No wrangler.toml file found. Continuing.
00:14:26.223	Detected the following tools from environment: 
00:14:26.223	Executing user command: curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.30/mdbook-v0.4.30-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build
00:14:26.245	  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
00:14:26.246	                                 Dload  Upload   Total   Spent    Left  Speed
00:14:26.455	
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
00:14:26.831	mdbook
00:14:27.065	
  0 5031k    0 32768    0     0  59463      0  0:01:26 --:--:--  0:01:26 59463
100 5031k  100 5031k    0     0  6130k      0 --:--:-- --:--:-- --:--:-- 18.1M
00:14:27.136	2024-05-02 16:14:27 [INFO] (mdbook::book): Book building has started
00:14:27.146	2024-05-02 16:14:27 [INFO] (mdbook::book): Running the html backend
00:14:36.384	Finished
00:14:36.385	Note: No functions dir at /functions found. Skipping.
00:14:36.385	Validating asset output directory
00:14:37.224	Deploying your site to Cloudflare's global network...
00:14:42.448	Uploading... (1254/1254)
00:14:42.449	✨ Success! Uploaded 0 files (1254 already uploaded) (0.65 sec)
00:14:42.449	
00:14:43.060	✨ Upload complete!
00:14:45.569	Success: Assets published!
00:14:46.752	Success: Your site was deployed!