2 分钟
从零手写一个 Next.js 博客(本站的技术栈与实现)
你现在看到的这个博客,是我从空目录开始手写的——没有用任何现成博客模板。这篇文章公开全部实现细节,也算给自己留一份"为什么这么设计"的备忘。
技术选型
- Next.js 16 + React 19 + TypeScript:生态成熟,SEO 开箱即用,以后加小工具/商城都是同一个框架;
- Tailwind CSS v4:原子类 + CSS 变量做主题,深浅色切换只需一个
darkclass; - unified/remark/rehype 管线渲染 Markdown,不引入 MDX、不引入 CMS——内容就是
content/posts/*.md,git 即后台。
依赖刻意保持在个位数,博客的核心是内容和排版,不是技术堆砌。
Markdown 管线
一篇文章从 .md 到 HTML 的流水线:
const processor = unified()
.use(remarkParse) // 解析为 AST
.use(remarkGfm) // 表格/任务列表/删除线
.use(remarkRehype) // 转 HTML 树
.use(rehypeSlug) // 标题加 id
.use(rehypeAutolinkHeadings) // 标题可点击锚点
.use(rehypeHighlight) // 代码高亮
.use(rehypeStringify); // 输出 HTML 字符串
有个细节:文章页右侧的目录(TOC)需要标题的 id。我单独用 remark-parse 遍历 AST 提取 h2/h3,再用 github-slugger 生成 slug——它和 rehype-slug 内部用的是同一个库,保证目录锚点能对上。
内容层
frontmatter 用 gray-matter 解析,字段极简:
---
title: 文章标题
date: 2026-10-03
tags: [Next.js, 建站]
summary: 一句话摘要,会出现在列表页和 SEO 描述里
---
draft: true 的文章在开发模式可见、生产构建自动剔除,方便写一半的东西先躺仓库里。中文阅读时长按 CJK 字符数(380 字/分钟)+ 英文词数(200 词/分钟)估算。
主题与配色
设计走"纸感编辑风":米白纸底 + 墨色正文 + 朱砂红做点缀色(链接 hover、选中高亮、标签)。所有颜色收敛为 6 个 CSS 变量,深色模式只是换了一组变量值:
:root { --accent: #c73e3a; --bg: #faf8f5; /* ... */ }
.dark { --accent: #f08080; --bg: #0f0d0c; /* ... */ }
防闪白的关键是在 <head> 里塞一段同步脚本,首帧渲染前就把 dark class 挂到 <html> 上。
站点功能清单
这些全部是标准 Next.js 能力,没用第三方服务:
| 功能 | 实现 |
|---|---|
| RSS 全文输出 | app/rss.xml/route.ts 动态生成 |
| sitemap / robots.txt | sitemap.ts / robots.ts 约定文件 |
| 站内搜索 | 预生成 JSON 索引 + 纯前端过滤 |
| 代码复制按钮 | 客户端渐进增强,给每个 pre 注入按钮 |
| 评论 | giscus(GitHub Discussions),配置留空即隐藏 |
| 统计 | umami 自托管,同样配置驱动 |
| SEO | 每篇文章独立 generateMetadata + OG 标签 |
为什么不直接用模板
模板(比如 tailwind-nextjs-starter-blog)很好,但我想得到三样东西:
- 对每一行代码的理解——以后要做分发的、要挂工具的、要接支付的,都得自己改;
- 恰好需要的功能——没有后台、没有数据库、没有用户系统,也就没有攻击面;
- 可迁移的判断力——亲手写一遍 unifed 管线,下次遇到任何渲染需求都不虚。
全部代码就在这个仓库里,欢迎 fork 自用。下一篇我会写怎么在这套底座上挂第一个小工具。