· 阅读约 1 分钟

这个站是怎么搭的

  • 系统设计
  • 站务

这是站点的第一篇文章,同时也是排版样张。确认样式没问题之后可以直接删掉它—— 删掉 content/posts/how-this-site-works.md,重新构建就没了。

为什么不用 Hexo 或 Hugo

作品集页面是手写的 HTML,栅格和留白都是按视觉稿一像素一像素调出来的。 套进任何一个博客框架,都要先把它拆成框架的模板语法,再和主题的默认样式打架。

所以这里反过来:框架只负责文章,不碰已经写好的页面

整个构建就是一个 build.mjs,两百来行,做四件事:

  1. content/posts/*.md,解析 frontmatter;
  2. 渲染成 public/writing/<slug>/index.html
  3. 生成 feed.xmlsitemap.xmlrobots.txt
  4. 把首页里 <!-- build:recent --> 标记区间的内容换成最新三篇。

手写的 index.htmlproject.html 除了那几个标记区间,一个字节都不会被动。

写一篇文章

content/posts/ 下新建一个 .md,文件名就是 URL:

# content/posts/scaling-inference.md → /writing/scaling-inference/
npm run dev   # 起监听,改完存盘自动重建

frontmatter 只有 titledate 是必填的:

---
title: 推理服务的三次重写
date: 2026-09-12
summary: 从单机脚本到多租户网关,每一次重写解决的都是上一次没想清楚的问题。
tags: [机器学习, 系统设计]
draft: true
---

draft: true 的文章不会被构建。想在本地预览草稿:

npm run build -- --drafts

数据在自己手里

能力 方案 部署位置
访问统计 Umami 同域 /_a,绕开广告拦截
评论 Waline comment.wenjunjia.cloud
静态托管 Caddy systemd,自动续期证书

统计不写 Cookie,也不做跨站追踪,所以不需要弹隐私横幅。 评论支持匿名,邮箱选填,只在有人回复时用来发通知。

用第三方服务省下来的时间,迟早会以数据被拿走、或者服务下线的形式还回去。 一台八块钱一个月的机器能解决的事情,不值得让渡数据。

代码块长这样

def reading_minutes(text: str) -> int:
    """中文按字、西文按词分别估算。"""
    cjk = sum(1 for ch in text if "一" <= ch <= "鿿")
    words = len(text.encode("ascii", "ignore").split())
    return max(1, round(cjk / 400 + words / 220))

行内代码是 --rootMargin: 400px 这种样子,链接是 Caddy 的文档 这种样子。


有问题直接在下面留言。

评论