工具应该安静地退到内容后面。
这个博客最初用 Hexo 搭建,换过主题,也攒下了一些文章。它一直能工作,但每次想改一点东西,都要先和主题配置、插件依赖、生成目录打交道。久而久之,写文章之前反而先要“维护博客”。
于是我花了些时间,把整个站点重写了一遍:不套前端框架,不依赖 Hexo,用 Node.js 把 Markdown 编译成纯静态 HTML,再交给 GitHub Pages。下面记录这次重制保留了哪些东西、是怎么做的,以及以后一篇文章从落笔到上线要经过哪些步骤。
这次重制,保留了什么
最不能丢的是旧链接。新站沿用原来的文章地址规则:
/年/月/日/文件名/
迁移过来的 41 篇文章、12 个分类和 10 个标签,文件名、日期和图片路径都保持原样,所以之前被搜索引擎收录的地址、别人收藏的链接仍然可以打开。域名也继续使用 blog.muyc.site,只是背后的生成方式换了。
在内容之外,我给自己定了几个目标:
- 构建和访问都要快,页面不依赖前端框架;
- 写作只关心 Markdown,不再和模板语法纠缠;
- 字体、配色、明暗主题和移动端都能自己控制;
- 发布流程可以重复执行,不依赖某台电脑上的 Hexo 环境;
- 旧文章不改 URL,新文章可以自己决定是否使用英文 slug。
不再依赖框架
新版没有前端框架,也没有打包器。构建时运行一个 Node.js 脚本,把 content/ 里的内容编译到 public/;浏览器最终拿到的只有 HTML、CSS 和少量原生 JavaScript。
整个项目只有四个运行依赖:marked 负责 Markdown 渲染,marked-highlight 和 highlight.js 负责代码高亮,js-yaml 解析文章开头的 front matter。除此之外没有再引入别的东西。
目录结构也很直接:
Blog/
├─ content/
│ ├─ posts/ # 所有文章,.md 文件
│ ├─ pages/ # 关于页等固定页面
│ └─ media/ # 图片资源
├─ src/
│ ├─ build.js # 静态站点生成器
│ ├─ dev.js # 本地预览服务器
│ ├─ lib/ # Markdown、模板、内容读取
│ ├─ styles/ # 全站样式与主题变量
│ └─ scripts/ # 主题、搜索、目录、灯箱等交互
├─ site.config.js # 标题、域名、导航、主题时间等配置
└─ public/ # 构建产物,不提交
一次 npm run build 大概只要半秒,过程分成几步:
- 扫描
content/posts/,解析每篇文章的 front matter 和正文; - 用 Marked 渲染 Markdown,同时给代码块加高亮;
- 套用统一的页面模板,生成文章页、首页、归档、分类和标签页;
- 生成搜索索引、RSS、站点地图和 404 页面;
- 把结果写入
public/,交给 GitHub Pages。
搜索也是静态的:构建时把所有文章整理成一份 search.json,浏览器在本地过滤,不需要后端,也不需要第三方搜索服务。
页面与阅读体验
视觉上换成了偏纸张质感的浅色背景,字体使用 MiSans Medium,通过 jsDelivr 加载;如果 CDN 不可用,会自动回退到 Inter、苹方、微软雅黑等系统字体。代码块仍然使用等宽字体,避免中英文混排时失去对齐。
主题默认跟随时间:晚上 19:00 到次日 07:00 使用深色,其余时间使用浅色。右上角的按钮可以在白天和夜间之间直接切换,选择会保存在浏览器里,下次访问继续沿用。如果在夜间手动切到白天,桌面端会在鼠标附近、手机端会在底部显示一条用眼提示,几秒后自动消失。
阅读相关的细节也重新做了一遍:
- 文章右侧有目录,手机上收进顶部折叠面板;
- 顶部显示阅读进度;
- 代码块带语言标签和一键复制;
- 图片懒加载,点击可以打开灯箱;
- 表格在窄屏下横向滚动,不挤压正文;
- 首页、归档、分类、标签和关于页都做了移动端适配;
- 支持键盘操作,也尊重系统的“减少动效”设置。
这些功能都不复杂,但只有在自己的模板里,改起来才足够顺手。
一篇文章是怎么写出来的
新文章就是 content/posts/ 下的一个 .md 文件。开头用一对 --- 包住元信息,后面直接写正文:
---
title: "在 Windows 上备份 WSL"
date: 2026-10-21 20:30:00
category: Windows
tags:
- Windows
- WSL
description: "记录一次 WSL 发行版的备份与恢复过程。"
slug: backup-wsl
draft: false
---
## 背景
这里开始写正文。
常用字段的含义:
| 字段 | 作用 |
|---|---|
title |
文章标题;不写时用文件名 |
date |
YYYY-MM-DD 或 YYYY-MM-DD HH:mm:ss,按 +08:00 解释 |
category |
单个分类;不写会归入“未分类” |
categories |
分类数组,优先级高于 category |
tags |
单个标签或 YAML 数组 |
description |
列表摘要、搜索描述和 RSS 描述 |
slug |
自定义 URL 段;不写就用文件名 |
draft |
设为 true 后不会出现在构建结果里 |
正文从 ## 开始。如果习惯在开头再写一个 # 标题,构建时会自动降级,避免页面上出现两个主标题。## 和 ### 会进入文章目录,标题尽量简短、唯一。代码块记得标注语言,例如 bash、js、python,这样才有正确的语法高亮和语言标签。
图片统一放在 content/media/ 下,用 /media/... 的绝对路径引用:

alt 末尾的 -w720 表示最大宽度 720px,避免大图在大屏上被拉得过宽。图片路径尽量只使用英文、数字、下划线和连字符,并且注意大小写:Windows 本地不区分,GitHub Pages 的 Linux 环境区分。
为了少写重复的 front matter,我在本地写了一个小脚本,按标题、分类和标签生成文章骨架。它只用于写作,不参与构建,也不会随站点发布。真正的内容还是自己写。
本地预览与构建
在项目根目录下执行:
npm install
npm run dev
打开 http://localhost:4321 就能预览。开发服务器会监听 content/、src/ 和 site.config.js,改动后自动重新构建,刷新浏览器即可。端口被占用时,可以在 PowerShell 里换一个:
$env:PORT=5000; npm run dev
发布前先构建一次:
npm run build
构建产物是 public/,它已经被 .gitignore 排除,不需要提交。构建时还会检查文章里引用的 /media/... 图片是否存在,缺失时给出提醒,但不会中断构建。
发布:让 Actions 去构建
源码放在 muchenh.github.io 仓库里,GitHub Actions 负责构建和部署。推送 main 后,工作流会依次执行:
- 检出仓库代码;
- 安装 Node.js 22;
npm ci安装锁定版本的依赖;npm run build生成public/;- 上传构建产物,交给 GitHub Pages 发布。
也就是说,仓库里保存的是源码,线上托管的是构建产物。public/ 不需要进 Git,页面也不是在本地生成后再手动上传。
在替换旧站之前,我先给原来的 main 打了一个 pre-static-rebuild 标签。以后如果发现新版本有问题,至少还能回到切换前的那一刻。仓库的 Settings → Pages → Source 需要选择 GitHub Actions;自定义域名仍然是 blog.muyc.site,DNS 里的 CNAME 指向 muchenh.github.io。
发布流程不需要个人访问令牌:GitHub Actions 使用仓库内置的 OIDC 权限完成 Pages 部署,令牌不应该出现在代码、配置或提交历史里。
日常更新文章的流程就是:
git add content/
git commit -m "post: 文章标题"
git push
等 Actions 里的 build 和 deploy 都变成绿色,刷新线上页面即可。更完整的部署步骤、DNS 配置和常见问题都整理在仓库的 README.md 里。
踩过的坑
旧链接不能随便动。 文章地址由日期、slug 和文件名决定。迁移时保留了原来的文件名和日期,新文章如果要一个稳定的英文地址,最好一开始就用 slug 固定下来,发布后不要再改。
返回顶部曾经失效。 一开始把 id="top" 放在固定在顶部的页头里,浏览器认为目标一直在视口内,点击页脚链接自然没有反应。把锚点移到 <body> 上之后才正常。
移动端的提示层会挡菜单。 用眼提示最初是一个浮层,在手机上可能正好盖住展开的导航。后来改成:切换主题前先收起菜单,手机端提示固定在底部,并避开安全区域。
图片在本地正常,线上 404。 大多数情况是文件名大小写不一致,或者图片没有提交。GitHub Pages 区分大小写,本地 Windows 不区分,这类问题只能在构建后逐条核对。
旧文章里可能藏着无效引用。 迁移时发现有一张旧图在原始目录里也不存在,构建会给出提醒。现在把它当成待办留着,不因为一张图阻断整个站点。
接下来
这次重制没有增加评论、统计之类的东西。对现在的我来说,博客最重要的还是三件事:能稳定打开、能舒服地读、能方便地写。剩下的功能,等真的需要时再加。
如果你也在维护一个很久没动的博客,或许不必急着换主题。先想清楚哪些部分是内容,哪些部分只是工具;把工具简化之后,写作会重新变得轻松。
再长的路,一步步也能走完;
再短的路,不迈开双脚也无法到达。