§ 09 — 用料清单
这个站是怎么搭的
全部写下来,包括做错的地方。既然要卖「我会建站」这句话,站本身就得经得起翻。
技术栈
| 层 | 选型 | 为什么 |
|---|---|---|
| 框架 | Astro,静态输出 | 没加 client 指令的内容原样就是 HTML。这是架构性质,不是配置技巧 |
| 样式 | Tailwind CSS v4 + 手写 token | token 管两个表面,Tailwind 管其余 |
| 动效 | 只用原生 CSS | View Transitions 和 scroll-driven animations 跑在 compositor 上,零 JS 开销,也影响不到爬虫读到的东西 |
| 内容 | Astro Content Collections | 构建期预渲染 |
| 图标 | astro-icon,内联 | 不用图标字体,不发 CDN 请求 |
| 客户端 JS | 0 字节 | 每次构建都验,不是口号 |
字体
拉丁文的标题和界面用 Archivo(OFL 1.1),自托管。 它本来就是为印刷和导视标识设计的字体——工作室的名字也是从导视来的。 数字、读数和代码用 IBM Plex Mono(OFL 1.1)。 两个都做了子集化并从自己的域名提供,因为 CSP 只允许 font-src 'self'。
中文正文走读者设备上的系统字体栈——苹方、鸿蒙黑体、微软雅黑。 这是刻意的:完整自托管一套中文字体是 MB 级的量,加载时的字体替换肉眼可见; 而中国用户设备上的系统中文字体质量本来就够好。 真需要特色中文字体的地方用 霞鹜文楷(OFL 1.1), 按实际用到的字精确子集化,几 KB 而已。
没有用 Geist。它的许可没问题,问题是辨识度——一上脸就知道出处。 同样的理由排除了 shadcn 的默认配色。
部署
静态产物由洛杉矶一台 VPS 提供,走 Cloudflare Tunnel,最外层是 Cloudflare 代理。 API 挂在同一个源的 /api/ 下,反代到一个手写的 Node 服务—— 同源意味着不用配 CORS,也没有跨站 cookie 的麻烦。
没有 CI。上线闸放在 deploy.sh 里,这是一个人的项目真能维护得住的地方。
发布之前跑什么
scripts/geo-check.mjs 跑在 dist/ 上——检查的是真实产物不是源码—— 任何一项致命就退出码非零:
- 逐页统计删掉 script/style 后的正文字数,卡下限
- 客户端 JavaScript 字节数,以及有没有外部脚本引用
- canonical 是否存在、唯一、且与本页 URL 一致
- hreflang 是否双向互指,
x-default是否落在真实页面 - 每段 JSON-LD 能否解析,
inLanguage与<html lang>是否相符 - 站内链接是否都指得到
- 每页一个
h1,title 与 description 齐全 - 对渲染后的文案做违禁词扫描
- sitemap 与 canonical 集合对账
- robots.txt 是否列出了该列的爬虫
做错的地方,留档
把字节当字符数
早期量自己生产站的正文,报出来是 14,524 字符。两个错叠在一起:wc -c 数的是字节;而只删 <script> 标签、 不删它内容的正则,会把 JSON-LD 和内联 JavaScript 的文本一起算进正文。 真实数字是 3,999。由此得出的「已接近满分」这个结论, 也跟着下调成了「结构合格、语料几乎为零」。
canonical 和 sitemap 差了一个斜杠
第一次绿灯的构建里,canonical 写的是 /zh,sitemap 写的是 /zh/。 canonical 指向一个会被重定向的地址,比不写 canonical 更糟。 构建输出里没有任何迹象——是把两个集合拿来对账才发现的。 上面第九项检查就是因为这个才有的。
差点抄了一份拦错东西的黑名单
一轮调研推荐了某个知名 AI 爬虫仓库里的 nginx-block-ai-bots.conf, 说它能拦训练类爬虫、同时放行引用类爬虫。把那十二行读完就会发现, 它是一条无差别黑名单:GPTBot、ChatGPT-User、ClaudeBot、Claude-User、 Claude-SearchBot 全部返回 403。 我们只从那个仓库取 UA 名单,其余一概不取。
用到的开源
Astro、Tailwind CSS、@astrojs/sitemap、@astrojs/mdx、astro-icon、Archivo、IBM Plex Mono、霞鹜文楷, 以及 ai-robots-txt/ai.robots.txt 的爬虫 UA 名单。
许可在采纳时逐个核过,部署时再卡一道——338 个生产依赖,全部宽松许可。 白名单保持严格,copyleft 一律不进;确实需要放行的走具名例外, 而不是放宽白名单本身。放宽了,下一个撞进来的 copyleft 包就会静默通过, 这道闸也就不再是闸了。
目前有一条这样的例外。@img/sharp-* 是Apache-2.0 AND LGPL-3.0-or-later——LGPL 那一半来自预编译二进制里 打包的 libvips,Astro 的图片处理用它。它只在构建期跑,库本身不被分发, LGPL 允许链接使用而不触及自有源码,而且一张被缩放过的图片 不是缩放器的衍生作品。