跳转至

建站手记:这个 wiki 是怎么一点点长起来的

这不是一篇教程,是一篇过程记录

我想把这个 wiki 从搭起来、到一点点折腾的过程留个底——做了什么、当时为什么这么选、踩了什么、现在卡在哪、又在想什么。具体某一步怎么配(比如 COS 那套),我另写了更细的一篇;这篇只串脉络,按时间往下记,做一段补一段。

所以它会一直没写完——这是故意的。


2026-06 · 先让它能上线

最开始的目标很朴素:有个自己的地方放笔记,能公开访问就行。

静态站点生成器选了 Zensical(Material for MkDocs 团队的新东西),内容写在 docs/ 下的 markdown 里,zensical build 出一堆静态文件到 site/

托管我没用服务器跑 nginx,而是直接丢腾讯云 COS(对象存储)。理由当时想得挺清楚:我那台服务器带宽小,而且我不想让流量都从服务器过一遍。COS 直出 + 自定义域名,用户访问时 DNS 直接指到 COS,服务器完全不在请求链路上。

这一段踩的坑不少(国内 bucket 默认强制下载、CNAME 该指哪个域名、HTTPS 证书怎么自动续……),都记在那篇细的里了,这里不重复。

结论是:跑通了。./deploy.sh 一行,构建 + 上传,https://wiki.liuhetian.work/ 能正常打开,证书也能自动续。到这里,"能用"这个目标达成了。


2026-06 · 顺手测了下速度,有点意外

站点能用之后,我有点好奇:现在这样直连 COS,到底快不快?

一开始我在部署用的那台 GPU 服务器上 curl 测,数字挺好看。但马上意识到不对——那台机器的网络出口跟普通用户根本不是一回事,它测出来的速度对真实访问没参考价值。

于是换了个思路,用 ping.chinaz.com 这种全国多节点的在线测速,让各地的监测点去访问我的首页,看延迟。这个才贴近真实用户。

测下来几个发现,有些跟我预想的不一样:

  • 连接本身不慢。 全国各地建连接基本都在 20–50ms,挺均匀。我本来以为"源站在成都、离得远的地方会明显慢",但北京、上海、深圳跟成都附近其实差不多,都在 0.3 秒上下。腾讯云这个接入覆盖比我想的好。
  • 慢的是 DNS 解析。 那一段冷解析要 0.15–0.3 秒,个别节点甚至 0.5–1 秒以上,是整个耗时里的大头。不过真实用户第二次访问 DNS 有缓存,会快很多,所以这条没那么吓人。
  • 个别地区会抽风。 深圳、大连、呼和浩特偶尔冒出 1–2 秒,洛阳那个节点直接超时。单点直连 COS,没有任何缓冲,遇到线路波动就这样。
  • 一个意外发现:压缩根本没开。 我看响应头,没有 Content-Encoding。也就是说我那几个大页面(有个 400 多 KB 的)是原样裸传的,没压缩。文本类的东西压一下能省一大半流量,这块白白浪费了。

那篇细的里我顺手记了 17KB 的首页测延迟够不够的纠结——对文档站来说,延迟比带宽重要,所以测延迟是对的,但小文件测不出真实下载带宽。

总的判断:自己用、国内访问,现在这速度(零点几秒打开)其实够了。 没有想象中那么糟。


2026-06 · 现在在想的事:要不要上 CDN

这一段还没有结论,是我当下的纠结,记下来。

测完速度,一个很自然的念头是:上个 CDN 是不是能更快、还能顺手把压缩开了?

我纠结的点在于——直连 COS 其实已经够用了。访问我这破 wiki 的人本来就没几个,一天下载量 50MB 都不到。纯从"快不快、值不值"看,CDN 不是刚需。

但我还是想做,理由有两个,而且我得对自己诚实:

  1. 顺手能解决那两个真实的小毛病:把没开的压缩开上、用边缘缓存抹掉那些偶发的卡顿和超时。
  2. 更主要的是想学。 部署、加速、DNS、证书、缓存这些前端/运维的东西我本来就不懂,而这个 wiki 是个绝好的练手对象——流量小不怕花钱(算了一下大概一个月几毛钱),自己的站不怕搞坏。用一个真实在线的项目把"部署 → 加速"这条链路亲手跑通,比看十篇教程都强。

所以这件事对我来说,学习的意义大于性能的意义

目前没什么卡点——域名本来就有备案,纯粹是这事还没排上手。

至于 CDN 具体怎么配、配完效果如何、又踩了什么坑——等我真做了再回来补。 现在还没动手,先不写,免得纸上谈兵。


2026-07 · CDN 还没动手,先把「AI 友好」做实

CDN 那事还没动,这段时间没闲着,回头收拾了一件从建站起就一直含糊的事:我到处说这个 wiki「对 AI 友好」,但它到底哪里友好?说不清楚。这个月做了三个决定,把它从口号变成几条能检查的机制。

第一件:文章定型成「一文一目录」。 起因是我发现自己犯了文档界的老毛病——文章里的代码块是从仓库复制粘贴出来的,deploy.sh 一改,文章里那份就成了过期的假货。解法分两层:仓库内用 snippet(--8<--)让文章直接引用真实文件,源头只有一份;发布时把真身也带出去——每篇文章从单个 .md 变成一个目录(index.md + assets/),assets/ 里放软链指向仓库里的真实文件,构建上传后软链变成真内容,线上的 AI 顺着 URL 就能 GET 到未压缩的源码。原来专门放副本的 cos-wiki-deploy-src/ 目录整个删掉了。顺手踩了个坑:docs/ 里出现 .yml 文件会把构建直接搞崩(pyproject.toml 倒没事),软链哪些文件得挑着来。

借着改结构,把 COS 那篇也重写了:原来是「第一步建 bucket、第二步……」的教程体,现在 index.md 只讲什么算 AI 友好、为什么这么设计,操作步骤全部下沉到实操手册。因为我意识到那篇真正想说的从来不是怎么部署。

第二件:给引用外部资料立了规矩。 重写那篇时引了 llms.txt 提案和 LLM Wiki 两份资料,一开始就贴个链接完事。后来想明白两个问题:链接会烂,而且线上的 AI 未必打得开外链。于是定成三步:真身下载进 assets/ 存档、正文摘关键原句、每处引用必须跟自己的分析——只贴不评等于没消化。这条写进了写作 skill,以后所有文章都按这个来。

第三件:交互 demo 也得符合同一套标准。 新开了个 Dashboard skill(后台页面的六种形状,每种配一个可以点的活 demo),顺势把「静态站里怎么放交互页」想清楚了:纯客户端的自包含单页放进 assets/,文章里 iframe 嵌入。几个看着反常的选择其实都冲着 AI 友好去:运行时库不走 CDN,vendor 进全站共享的 /vendor/(国内不抖、断外网也能跑);React 钉在 18 的 UMD 构建配 htm,免掉 JSX 编译(19 起不发 UMD 了);demo 逻辑坚决不压缩——压缩了 AI 就读不懂了。一个静态对象存储站能承载「可玩的组件库」,这件事比我预想的顺利。

回头看,这个月其实是把首页那句「对 AI 友好」从形容词变成了名词:内容是 markdown 源、代码是真身软链、引用有存档、demo 源码可读。CDN 还是没动,往后有进展再补。


2026-07 · Dashboard 从六个旗舰扩到 36 形状全成文

写完六个旗舰形状那阵,本来的想法是"每组挑一个做示范就行了"。真跑起来发现这不成立——每篇文章都在指同一个开源蓝本 open-dashboard 的另外一个 reference,读者点开某个链接想接着看,结果又跳出 wiki。我这才承认:要么全做、要么别做,"六个旗舰"的定位本质上是半成品。

一次性把剩下的 17 篇写完,加上前 18 篇(其中"批量操作"合到表格、"空态"合到反馈状态,故 36 形状对应 35 篇文章)。每篇都是同一个模板:一段定位 → iframe demo → 试这几下 → 「规矩」节(蓝本 Invariants 中文提炼)→ 蓝本永链一行 → demo 源码折叠。每篇配一个 200-300 行的自包含 React 单页 demo,交互点严格对齐蓝本不变量。

顺手做了另一件更重要的事:把原来本地存的 84 篇蓝本 md 全删了。 起初我按 引用三步规矩把它们完整镜像进 assets/,理由是"AI 未必打得开外链、真身存档保险"。做完 36 篇之后回头看这个决定不成立:① Invariants 已经在每篇文章的「规矩」节里消化成中文了,读者根本不需要点开原文;② demo 就是完整实现,比原文里的 tsx 片段还全;③ 蓝本仓库是活跃的开源项目,钉 commit 的 GitHub 永链不会失效。存这 84 份原文纯粹在给 COS 加对象数。删完只留三份:PATTERNS.md(36 形状总目录 + 共享契约,切不进任何单篇)、backends/CONTRACT.md(前后端线协议)、加一份 MIRROR.md 说明为什么这么做。文章里的引用改成文末一行> 蓝本:[<ref>.md](<钉 commit 的永链>)

这条例外我写进了引用三步规矩活跃开源仓库 + 钉 commit 永链,可以替代真身存档,前提是原文的关键契约已经在正文里消化成自己的语言、读者不依赖点开链接。规矩不是死的,触发条件也是规矩的一部分。


2026-07 · 仓库瘦身:二进制赶出 git

写着写着发现仓库越来越沉——demo 配图、视频、3D 模型、MathJax/ECharts 这些大文件全进了 git,clone 一次拖一堆跟"知识"无关的字节。收拾的思路是一句话:git 里只放文本。二进制按来源分两类,各走各的路:

  • 能从公网按版本重新拉到的(MathJax、ECharts 这类重型 vendor):不入库,scripts/fetch-vendor.sh 钉死版本号从 CDN 恢复,跟 mkdocs.yml 里的引用一一对应
  • 自己产的媒体(图 / 视频 / glb / 音频 / pdf):不入库,scripts/backup-media.sh 备份到一个独立的 COS 桶——跟部署桶隔离,只增不删、同名才覆盖,桶里的历史文件永远留着;down 拉回时按本地目录白名单走,已删文章的历史资源留在桶里不回来。svg 是文本,照常进 git

两类都出库之后,把 git 历史重置成一个 commit,甩掉历史里已经进去的大对象。代价是丢了提交历史——可以接受,这个仓库的"历史"本来就记在这篇手记里,不在 commit message 里。新机器 clone 后三步恢复:uv syncfetch-vendor.shbackup-media.sh down,写进了 README。


2026-07 · 规范全部收进 wiki 本体,然后把 AI 的记忆清了

干活的 AI 有个跨会话的项目记忆,几个月攒了 11 篇、快 30KB——引用怎么写、风格文章什么格式、踩过什么坑。这个月让它自己盘点了一遍:大半内容 wiki 里早就有了,记忆只是快捷索引;但有几条规矩只活在记忆里,wiki 上查不到。这不对劲——一个号称 AI 友好的知识库,自己的写作规范居然要靠库外的私有记忆才凑得齐。

于是把缺的都补进 wiki 本体:风格文章的收录格式(35 行模子、prompt 式要点)进了前端风格收集的索引页;写作规范新增了文风一节(先结论后展开、统一术语、规矩必须带活例、没做的事就说没做)和一条新规矩——收录外部 git 仓库只取核心文件,不整仓 clone,不追求完整、追求核心,因为人也要读,要完整自己顺永链去 GitHub;COS 那篇文末补了「下一步」:个人 IP(AI 可执行的 style spec)和写作管线,两件没做的事先记下。

补完之后,记忆全删。以后规范只有一个来源:wiki 自己。顺手把这篇手记也从独立文章挪进了 AI 友好知识库、跟实操手册同级——它记的本来就是这个知识库怎么长起来的。


2026-07 · 二进制的终局:自制 mini LFS,图片跟文字一条链路

上一段的双轨方案(vendor 靠 CDN 脚本恢复、媒体靠手动 backup-media.sh)跑了没几周就露出毛病:备份靠人记得跑,git 状态和媒体状态各走各的会漂移,副机传张图还得 rsync 对路径。回头看,根子在"二进制不进 git"这个前提立错了——想要的从来不是"不进 git",而是"不进 git 历史撑大仓库"

git-lfs 恰好就是这个语义:git 里只存几十字节的指针,真身放外面。而且它的存储后端是可插拔的——不必用 GitHub 的 LFS 服务(要花钱、在自己体系外),写一个 standalone custom transfer agent 就能把真身指到自己的 COS 备份桶:git 在 push/pull 时把它当子进程拉起,stdin/stdout 上四种 JSON 事件(init/upload/download/terminate),用完即退,没有常驻服务。真身按 lfs/ab/cd/<sha256> 内容寻址落桶,天然去重、幂等、历史版本永远留着。~140 行 Python,凭证复用 .env,SDK 是 coscmd 本来就带的——零新依赖。

vendor 的判断也顺势翻案:上一段把 MathJax/ECharts 定性为"能从公网按版本重拉的",但恢复源 jsdelivr 在国内时好时坏,"可再生"的前提并不牢。现在第三方真身(MathJax/ECharts/three/react)连同这次才发现的漏网字体(woff2,居然是历史里最大的二进制)一起进了 LFS——后端是自己的桶,可达性自己说了算。fetch-vendor.sh 从"恢复工具"降级成"升级工具"。

然后第二次重置历史。跟上次不同的是:上次只是甩包袱,规则没变,包袱注定再攒;这次 .gitattributes 把所有二进制拦在指针形态,git 历史从机制上不会再胖——重置后 1.59 MiB、一个 commit,以后加多少图都只涨指针。新机器恢复也从"三步各走各的"收敛成一条 git 链路:clone → setup-lfs.shgit lfs pull。细节和脚本真身沉在实操手册·资源备份

待续。