折腾了一周,终于用 Next.js 搭好了博客

一直想搭个人博客,拖了好久终于动手了。
之前试过 WordPress(太重),用过 Hexo(主题折腾起来麻烦),最后还是决定用 Next.js 自己撸一个。虽然花了一周时间,但结果还挺满意的。
今天分享一下我的搭建过程,希望能帮到同样想搭博客的朋友。
为什么选择 Next.js + Vercel
说实话,选择这个组合主要是因为:
Next.js 的优势:
- 基于 React,我本来就熟悉
- SSG(静态生成)对博客来说性能很好
- 开发体验不错,热重载很快
- 社区活跃,遇到问题容易找到解决方案
Vercel 的优势:
- 部署简单到爆,连接 GitHub 就行
- 免费额度对个人博客够用
- CDN 全球加速,访问速度快
- 自动 HTTPS,省心
最重要的是,这两个是一家公司的产品,集成度很高,基本不会有兼容性问题。
开始搭建
环境准备
首先确保你有:
- Node.js(我用的是 v18)
- Git
- 一个 GitHub 账号
创建项目
npx create-next-app@latest my-blog
安装过程中会问你一堆问题,我的选择是:
- TypeScript: Yes(强烈推荐)
- ESLint: Yes
- Tailwind CSS: Yes(样式写起来方便)
- src/ directory: Yes
- App Router: Yes(新版本的路由方式)
- import alias: Yes
创建完成后:
cd my-blog
npm run dev
打开 http://localhost:3000
,看到 Next.js 的欢迎页面就说明成功了。
设计文章存储方式
我选择用 Markdown 文件存储文章,原因很简单:
- 写作体验好,专注内容
- 版本控制友好
- 迁移方便,不绑定特定平台
在项目根目录创建 _posts
文件夹,然后创建第一篇文章 hello-world.md
:
---
title: "我的第一篇博客"
date: "2025-07-01"
excerpt: "终于有自己的博客了!"
---
这是我用 Next.js 搭建的第一篇博客文章。
虽然过程有点折腾,但结果还是很满意的。
解析 Markdown 文件
需要安装几个包来处理 Markdown:
npm install gray-matter remark remark-html
gray-matter
:解析文章头部的元数据remark
系列:把 Markdown 转成 HTML
创建 src/lib/posts.ts
来处理文章数据:
import fs from "fs";
import path from "path";
import matter from "gray-matter";
import { remark } from "remark";
import html from "remark-html";
const postsDirectory = path.join(process.cwd(), "_posts");
export function getSortedPostsData() {
const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map((fileName) => {
const id = fileName.replace(/\.md$/, "");
const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, "utf8");
const matterResult = matter(fileContents);
return {
id,
...(matterResult.data as { title: string; date: string; excerpt: string }),
};
});
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1;
} else {
return -1;
}
});
}
export async function getPostData(id: string) {
const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, "utf8");
const matterResult = matter(fileContents);
const processedContent = await remark().use(html).process(matterResult.content);
const contentHtml = processedContent.toString();
return {
id,
contentHtml,
...(matterResult.data as { title: string; date: string; excerpt: string }),
};
}
修改首页显示文章列表
修改 src/app/page.tsx
:
import { getSortedPostsData } from "@/lib/posts";
import Link from "next/link";
export default function Home() {
const allPostsData = getSortedPostsData();
return (
<main className="max-w-4xl mx-auto px-4 py-8">
<header className="mb-12">
<h1 className="text-4xl font-bold mb-4">我的博客</h1>
<p className="text-gray-600">记录学习,分享思考</p>
</header>
<section>
<div className="space-y-8">
{allPostsData.map(({ id, date, title, excerpt }) => (
<article key={id} className="border-b pb-8">
<h2 className="text-2xl font-semibold mb-2">
<Link href={`/posts/${id}`} className="hover:text-blue-600 transition-colors">
{title}
</Link>
</h2>
<time className="text-sm text-gray-500 mb-3 block">{date}</time>
<p className="text-gray-700">{excerpt}</p>
</article>
))}
</div>
</section>
</main>
);
}
创建文章详情页
创建 src/app/posts/[id]/page.tsx
:
import { getPostData, getSortedPostsData } from "@/lib/posts";
import Link from "next/link";
export async function generateStaticParams() {
const posts = getSortedPostsData();
return posts.map((post) => ({
id: post.id,
}));
}
export default async function Post({ params }: { params: { id: string } }) {
const postData = await getPostData(params.id);
return (
<main className="max-w-4xl mx-auto px-4 py-8">
<Link href="/" className="text-blue-600 hover:underline mb-8 inline-block">
← 返回首页
</Link>
<article>
<header className="mb-8">
<h1 className="text-4xl font-bold mb-4">{postData.title}</h1>
<time className="text-gray-500">{postData.date}</time>
</header>
<div className="prose prose-lg max-w-none" dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article>
</main>
);
}
部署到 Vercel
这是最爽的部分,真的超级简单:
-
推送代码到 GitHub
git init git add . git commit -m "Initial commit" git remote add origin https://github.com/你的用户名/my-blog.git git push -u origin main
-
连接 Vercel
- 去 vercel.com 用 GitHub 账号登录
- 点击 "Add New..." → "Project"
- 选择你的博客仓库
- 点击 "Deploy"
就这样,几分钟后你的博客就上线了!Vercel 会给你一个 .vercel.app
的域名。
我踩过的坑
坑 1:Tailwind 样式不生效
刚开始我的 Tailwind 样式总是不生效,后来发现是 tailwind.config.js
的 content
配置有问题。
正确的配置应该是:
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
坑 2:文章内容样式丑
Markdown 转换后的 HTML 默认没有样式,看起来很丑。我用了 @tailwindcss/typography
插件:
npm install @tailwindcss/typography
然后在 tailwind.config.js
中添加:
plugins: [require("@tailwindcss/typography")],
文章内容用 prose
类包裹就有漂亮的排版了。
坑 3:部署后页面 404
本地开发正常,部署后文章详情页 404。原因是我没有正确配置 generateStaticParams
,导致页面没有被静态生成。
后续优化
现在博客基本功能有了,但还可以继续优化:
- SEO 优化:添加 meta 标签、sitemap
- 评论系统:考虑集成 Giscus 或 Utterances
- 搜索功能:可以用 Algolia 或者简单的客户端搜索
- RSS 订阅:方便读者订阅
- 深色模式:现在流行的功能
总结
用 Next.js 搭博客确实比想象中简单,特别是有了 Vercel 的加持。虽然前期需要一些学习成本,但后期的可扩展性和性能都很不错。
最重要的是,有了自己的博客,写作的动力也更足了。毕竟,程序员不写博客,就像厨师不做菜一样。
如果你也想搭个人博客,强烈推荐试试这个方案。有问题的话,欢迎交流!
关注微信公众号

扫码关注获取:
- • 最新技术文章推送
- • 独家开发经验分享
- • 实用工具和资源
💬 评论讨论
欢迎对《折腾了一周,终于用 Next.js 搭好了博客》发表评论,分享你的想法和经验