#先说结论

不是说框架不好。Hexo 和 Hugo 我都用过,体验都很好。

但这次做站的时候,我发现我的需求其实只有一句话:

把 content/posts/*.md 变成 dist/ 下的一堆 HTML,顺便生成索引页、归档页和 RSS。

这件事,现成框架给了我一大坨我不需要的东西:主题系统、插件机制、标签分类的七种变体、几十个依赖。而我实际用到的 Markdown 语法,不超过十种。

#权衡

现成框架 自建脚本
初始上手 装依赖、选主题、改配置 打开 build.js 改配置
依赖数量 50 ~ 300 个包 0 个
攻击面 每次 npm audit 都有东西 不存在
产物大小 主题带一堆用不上的 CSS/JS 完全可控,首页 12KB
定制自由度 受主题结构限制 想怎么改怎么改
十年后还能跑吗 看依赖树 一个 Node 文件,能跑

最后一条是我最在意的。一个纯静态博客的生命周期可能有十年,而 node_modules 里的某个包在第三年就会因为 Node 版本升级而跑不起来。

#实际写了多少代码

  • lib/markdown.js —— 约 200 行,Markdown → HTML
  • lib/templates.js —— 约 280 行,页面拼装
  • build.js —— 约 200 行,文件遍历 + 索引 + RSS/sitemap
  • assets/style.css —— 约 600 行,双主题样式

加起来 1200 行出头,全是我自己写的,没有一行第三方代码。

对比一下:用 Hexo 建站,光 node_modules 就有 180MB,构建产物还带着一个 400KB 的 jQuery。

#Markdown 解析器要写多少

这是我最初觉得最麻烦的部分,写完发现比想象的简单。核心就是一个行扫描器:

while (i < lines.length) {
  const line = lines[i];

  if (/^```/.test(line)) { /* 代码块,吃到下一个 ``` 为止 */ }
  else if (/^\|/.test(line) && isTableDivider(lines[i + 1])) { /* 表格 */ }
  else if (/^(#{1,6})\s+/.test(line)) { /* 标题,顺手收集 TOC */ }
  else if (/^>\s?/.test(line)) { /* 引用,递归调用自己 */ }
  else if (/^[-*+]\s+/.test(line)) { /* 无序列表 */ }
  else if (/^\d+\.\s+/.test(line)) { /* 有序列表 */ }
  else if (!line.trim()) { /* 空行,收尾段落 */ }
  else { /* 普通文本,进缓冲区 */ }

  i++;
}

行内规则(粗体、斜体、链接、图片、行内码)用几个正则就够了。关键是先把行内代码抽出来占位,处理完再放回去 —— 否则代码里的 * 会被当成斜体标记。

const codes = [];
out = out.replace(/`([^`]+)`/g, (_, c) => {
  codes.push(c);
  return `\u0000CODE${codes.length - 1}\u0000`;
});
// ...处理其他规则...
out = out.replace(/\u0000CODE(\d+)\u0000/g, (_, i) => `<code>${codes[+i]}</code>`);

这个技巧在很多 Markdown 实现里都能用。

#顺手做的一些事

写的时候顺手加了些小功能,都是"有需求时才加"的:

  • 目录(TOC) —— 解析标题时顺手收集,渲染成侧边悬浮目录
  • 深浅色双主题 —— 一个 data-theme 属性 + localStorage,十几行 JS 搞定
  • 阅读时长 —— 中文按 400 字/分钟,英文按 200 词/分钟算
  • RSS / sitemap / robots —— 都是纯文本模板,几十行
  • 标签页 / 分类页 —— 复用文章卡片组件

#什么时候该用框架

如果你:

  • 不想碰构建流程
  • 需要评论、搜索、订阅等动态功能
  • 团队里有人不写代码

那用框架,别造轮子。Hexo + NexT 主题,五分钟就能上线。

但如果像我一样,需求明确到"就是 Markdown 转 HTML",而且你享受那种「整个站只有我知道它怎么工作」的踏实感 —— 那自己写一个真的挺爽。

#一个隐藏福利

自己写的好处是:站点永远不会"炸"。

没有依赖升级导致的构建失败,没有主题版本和 Node 版本不兼容,没有某天早上发现站打不开了还要去翻 issue。

一个纯静态 HTML 的目录,扔在任何能跑 Nginx 的地方都能跑。这才是"网站"最该有的样子。