双击 index.html,那就是你的豆瓣
站点生成器做完了,sample.doubak.com 上线。以及一个把用户自己写的字悄悄吃掉的 bug。
整条链路今天第一次从头通到尾:抓取 → 档案 → 解析 → 一个能打开的网站。 成品放在 sample.doubak.com, 用的是作者自己的豆瓣数据——2940 条标记、3394 条广播、4694 个页面、 2921 张封面加 124 张自己上传的图,一共 150 MB。
整个仓库下下来,双击 index.html 就能翻,不用起服务器,也不用联网。
页面里没有一个 src 指向站外——封面和图片的字节都从档案里导了出来。
这一条是整个项目的验收线:一份要豆瓣还在才能看的备份,不叫备份。
Markdown 才是产物,HTML 只是它的一个消费者
生成器输出的是 Markdown + YAML front matter,然后交给现成的静态站生成器渲染。
npm run md 出的那棵树里没有任何 Hugo 专属的东西(有一个测试专门守这条),
换 Astro / Eleventy / Jekyll 都能接。自带的 Hugo 骨架只有五个文件,
之所以只有五个,就是为了让它能被删掉——删了换任何一个现成主题,
每一个主题生态都变成它的模板库。
Hugo 本身是按固定版本下载官方二进制、对着仓库里写死的 SHA-256 校验,
不是一个 npm 依赖。因为 hugo-bin 会带来几百个间接包,
而一个「重建需要几百个包」的存档站点,跟它自己的前提是矛盾的。
然后是那个真正要紧的 bug
用户写的短评、日记正文,是要放进 Markdown 页面正文的。而豆瓣的短评里满是 Markdown 的活跃字符。拿 2831 条本人写的文本量了一遍:62 条渲染出来和原文不一样, 其中两条直接丢内容。
_(:з」∠)_ → <em>(:з」∠)</em> 下划线被吃掉(24 处)
From <May December> → From 片名整个消失
第二条是要命的那种:页面上什么都不剩,没有任何痕迹说明这里曾经有字。 一个会悄悄改写你写过的东西的存档,比没有存档更糟——因为它看起来是可信的。
修法是转义,不是打开 unsafe = true——那等于在一个要发到 GitHub Pages 的站点上
执行来自用户文本的 HTML。转义规则拿真实的 Hugo 验了 18 个用例,逐字节对回去。
其中有一条我一开始推错了,被构建当场纠正:
有序列表要转义的是那个点,不是数字——CommonMark 不认转义的数字。
永远不要把链接规则写死在 Markdown 里
交叉链接写成 ../movie/123.md——一个指向文件的路径,
最终 URL 长什么样交给静态站生成器决定。
第一版写的是 [标题](/movie/123/),结果一打开
uglyURLs 就全断,在 file:// 下每个链接都跳到目录列表。
还有一条同族的:relativeURLs 和 uglyURLs 必须一起开。
当时只开了前一个,然后我写下「双击 index.html 可以用」——页面确实打开了,
而里面每一个链接都是坏的。
验证导航要把链接解析到文件,不是看页面渲染出来没有。
发布前先演一遍
npm run deploy 把成品铺进一个仓库根目录,GitHub Pages 直接能发。
它做两件容易被忽略的事:
- 预演。明确列出哪些内容会变成公开的。别人的名字和短评在广播、 日记回复里到处都是,对这个受众里的一部分人来说,有些还是政治敏感的—— 所以第三方内容默认排除,而且发布前你能先看清楚。
-
把旧文件清干净(只保留
CNAME/LICENSE/README/.git)。留下来的旧页面是幽灵页: 有固定链接、打得开、看起来像真的,而数据里早就没有它了。