写在前面
大家好,我是 XFffff,一名高中生。这是我第一次尝试搭建自己的个人博客。说实话,一开始我对前端开发了解得不多,只是觉得有个自己的博客很酷,可以记录学习和生活。没想到这个过程比我想象的要复杂得多,但也更有趣。
这篇文章记录了我从零开始搭建博客的完整过程,包括遇到的各种问题和解决方法,特别是今天(2月15日)一整天的性能优化历程。希望能帮到和我一样想做博客的同学。
第一步:选择技术方案
一开始我很迷茫,不知道该用什么技术。在网上搜索了很久,看了很多教程,最后决定用这套方案:
| 组件 | 选择 | 为什么选它 |
|---|---|---|
| 框架 | Next.js 15 | 听说很流行,而且可以生成静态网站 |
| 样式 | Tailwind CSS 3.4 | 不用写太多 CSS,直接用类名就行 |
| 内容 | MDX | 可以用 Markdown 写文章,简单方便 |
| 部署 | Cloudflare Pages | 免费!而且速度快 |
| 包管理 | pnpm | 比 npm 快,节省空间 |
为什么不用 Vercel?
虽然很多教程都推荐 Vercel,但我选择了 Cloudflare Pages,因为:
- 完全免费,没有流量限制
- 全球 CDN,访问速度快
- 和域名管理在一起,方便
第二步:找到合适的模板
作为第一次做项目,我不可能从零开始写所有代码。在 GitHub 上找了很久,最后发现了 mengke.me 这个开源项目。
这个项目功能很完善:
- ✅ 支持 Markdown 写文章
- ✅ 有深色模式
- ✅ 可以搜索文章
- ✅ 自动生成 RSS
- ✅ 手机和电脑都能正常显示
我把它 Fork 到自己的 GitHub,然后开始改造。
# 克隆项目到本地
git clone https://github.com/WXFffff666/my-blog.git
cd my-blog
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev打开 http://localhost:3434,看到页面成功显示,心里特别激动!
第三步:个性化定制
修改个人信息
第一件事就是把模板里的信息改成自己的。主要修改这几个文件:
1. 个人信息(data/author-info.ts)
export const AUTHOR_INFO = {
name: 'XFffff',
description: '一个热爱学习的高中生',
email: '[email protected]',
identity: 'Student | Learning',
address: {
city: 'Guilin, China',
flag: 'flag-china',
timeZone: 8,
},
social: {
github: 'https://github.com/WXFffff666',
},
}注意:不要填真实姓名、手机号、详细地址这些敏感信息!
2. 网站信息(data/site-metadata.ts)
export const SITE_METADATA = {
title: 'XFffff 的个人博客',
author: 'XFffff',
siteUrl: 'https://blog.the37777777.top',
locale: 'zh-CN',
language: 'zh-CN',
}写第一篇文章
在 data/blog/202602/ 目录下创建 Hello_World.mdx:
---
title: 'Hello World - 我的第一篇博客'
date: '2026-02-12'
tags: ['随笔']
draft: false
summary: '这是我的第一篇博客文章!'
---
## 你好,世界!
这是我的第一篇博客文章。从今天开始,我要记录自己的学习和成长。
希望能坚持下去!保存后刷新页面,文章就出现了!那一刻真的很有成就感。
第四步:部署到 Cloudflare Pages
本地运行成功后,就要部署到网上了。这一步遇到了不少问题。
配置静态导出
Cloudflare Pages 需要静态文件,所以要修改 next.config.js:
const nextConfig = {
output: 'export', // 静态导出
images: {
unoptimized: true, // 必须禁用图片优化
},
}遇到的第一个错误
运行 pnpm build 时报错:
Page "/snippets/[...slug]" is missing "generateStaticParams()"
原因:项目里有个 snippets 目录,但我还没有添加任何内容。
解决:直接删除 app/snippets/ 目录,等以后有内容了再加回来。
创建 Cloudflare Pages 项目
- 登录 Cloudflare Dashboard
- 左侧菜单 → Workers 和 Pages
- 点击 创建 → 选择 Pages 标签页
- 连接 GitHub 仓库
重要配置:
| 配置项 | 值 |
|---|---|
| 框架预设 | Next.js (Static HTML Export) |
| 构建命令 | pnpm build |
| 构建输出目录 | out |
绑定自定义域名
Cloudflare 会给一个 xxx.pages.dev 的临时域名,但我想用自己的域名。
- 进入 Pages 项目 → 自定义域
- 添加域名
blog.the37777777.top - Cloudflare 自动添加 DNS 记录
- 等待 5-10 分钟生效
访问自己的域名,博客成功上线了!那一刻真的超级激动!
第五步:深色模式渲染优化(今天的重头戏)
博客上线后,我发现深色模式有严重的渲染问题:快速滚动时,文字会闪烁消失,非常卡顿。这个问题困扰了我一整天,但最终找到了解决方案。
问题现象
- 在深色模式下快速滚动页面
- 部分文字会闪烁、消失
- 有些文字清晰,有些文字模糊
- Footer 的文字清晰,但首页的文字模糊
排查过程
尝试 1:移除 PageTransition 的 transform
我发现 PageTransition 组件使用了 translate-y-2,这会创建 GPU 渲染层。
// 修改前
<div className="translate-y-2 opacity-0">
// 修改后
<div className="opacity-0">结果:❌ 还是会闪烁
尝试 2:移除所有动画的 transform
检查了所有动画,发现很多地方使用了 transform:
/* CSS 中的动画 */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(20px); /* ← 问题所在 */
}
}全部改成只用 opacity:
@keyframes fade-in-up {
from {
opacity: 0;
}
to {
opacity: 1;
}
}结果:❌ 还是有问题
尝试 3:移除 Greeting 的 bg-clip-text
发现 Greeting 组件使用了 bg-clip-text 和渐变动画:
// 修改前
<div className="bg-clip-text text-transparent animate-text-shimmer">
// 修改后
<div className="text-violet-600">结果:❌ 还是不行
尝试 4:对比原作者项目(找到根本原因!)
我仔细对比了原作者的项目,发现了关键差异:
我的配置:
// layout.tsx
<body className="dark:bg-black"> // 纯黑 #000000
// background.tsx
<div className="dark:bg-[#000000]"> // 纯黑
// tailwind.config.js
dark: {
bg: '#050505', // 极致深黑
}原作者的配置:
// layout.tsx
<body className="dark:bg-dark"> // 深灰色
// tailwind.config.js
dark: '#1f1f1f', // 深灰色,不是纯黑!发现问题:纯黑背景(#000000 或 #050505)在某些浏览器和操作系统上会导致字体渲染问题!
最终解决方案
将所有深色背景从纯黑改为深灰色 #1f1f1f:
// layout.tsx
<body className="dark:bg-dark-bg">
// background.tsx
<div className="dark:bg-[#1f1f1f]">
// tailwind.config.js
dark: {
bg: '#1f1f1f', // 深灰色
}结果:✅ 完美解决!深色模式滚动完全流畅,不再闪烁!
为什么深灰色不会糊?
#1f1f1f虽然看起来也很黑,但它不是纯黑- 这个颜色值在浏览器渲染引擎中不会触发某些特殊的优化路径
- 原作者也是用这个颜色,证明它是稳定可靠的
教训:有时候问题的根源不在代码逻辑,而在一些看似无关紧要的细节(比如背景颜色)。
第六步:图片加载优化
解决了深色模式问题后,我发现快速滚动时,下面会出现白条(内容还没渲染出来)。这是图片加载慢导致的。
优化方案
1. 预加载首屏图片
// latest-posts.tsx
{
posts.map((post, index) => <BlogCard key={post.slug} post={post} priority={index < 3} />)
}前 3 篇文章的图片使用 priority={true},浏览器会优先加载。
2. 添加渐变占位符
// blog-card.tsx
<div className="relative h-48 w-full overflow-hidden bg-gradient-to-br from-gray-100 to-gray-200 dark:from-[#121212] dark:to-[#1a1a1a]">
<Image src={coverImage} alt={title} priority={priority} />
</div>图片加载前显示渐变背景,视觉上更平滑。
3. 优化图片淡入效果
// image.tsx
<NextImage
className={clsx('transition-opacity duration-300', loaded ? 'opacity-100' : 'opacity-0')}
onLoad={onLoad}
/>图片加载完成后淡入显示,更自然。
结果:✅ 快速滚动时不再出现明显的白条!
第七步:导航栏交互优化
博客基本完善后,我开始优化细节。发现导航栏 hover 时有个放大动画,但放大过程中会模糊。
问题分析
导航栏使用了 hover:scale-[1.005],scale 变换会导致字体模糊。
尝试的方案
方案 1:移除 scale,改用阴影 + 上移
<nav className="hover:shadow-xl hover:-translate-y-0.5">结果:✅ 完全不会模糊,但上移有点突兀
方案 2:保留 scale,添加 GPU 优化(最终方案)
<nav
className="hover:scale-[1.005]"
style={{
willChange: 'transform',
transform: 'translateZ(0)',
backfaceVisibility: 'hidden',
WebkitFontSmoothing: 'subpixel-antialiased',
}}
>GPU 优化原理:
willChange: 'transform'- 提示浏览器会有变换transform: translateZ(0)- 强制创建 GPU 渲染层backfaceVisibility: 'hidden'- 隐藏背面,提升性能WebkitFontSmoothing: 'subpixel-antialiased'- 亚像素级抗锯齿
结果:✅ 保留了放大效果,模糊感大幅减少!
经验总结
1. 技术选型很重要
- Next.js 静态导出性能很好,适合博客
- Cloudflare Pages 免费额度够用,速度也快
- 找一个好的开源模板可以节省很多时间
2. 遇到问题不要慌
- 先对比原作者的代码,找出差异
- 逐步修复,每次只改一个地方
- 分别测试电脑端和移动端
- 善用浏览器开发者工具
3. 性能优化要系统思考
- 深色模式问题可能是背景颜色导致的
- 图片加载要预加载首屏内容
- GPU 优化可以减少 transform 的模糊感
- 不要过度使用动画和特效
4. 细节决定体验
- 背景颜色不要用纯黑,用深灰色
- 图片要有占位符,避免白屏
- 动画要流畅,不能卡顿
- 移动端和电脑端要分别优化
5. 学会阅读源码
- 遇到问题时,对比原作者的代码
- 理解每一行代码的作用
- 不要盲目复制粘贴
- 学会举一反三
后续开发:从初版到液态玻璃
博客上线之后,我以为可以歇一歇了。结果完全停不下来——每天都能想到新功能要加,新 bug 要修。从 2 月到 4 月,这个博客经历了一次又一次的迭代,最终变成了现在的样子。
功能大爆发
上线后的两个月里,我一口气加了 11 个新功能。说实话,有些功能是我自己想要的,有些是看到别人的博客觉得「这个好酷,我也要」。
阅读进度条 + TOC 增强。 文章顶部加了一个进度条,滚动的时候能看到读了多少。目录(TOC)也做了增强,在手机上变成了一个抽屉式的侧边栏,不会挡住正文。
相关文章推荐 + 系列导航。 每篇文章底部会推荐相关的文章,算法是根据标签重合度打分的。还加了「文章系列」功能,比如这篇和 TimeMark 那篇就属于「我的开发之旅」系列,可以按顺序浏览。
Sandpack 可运行代码块。 这个是我最喜欢的功能之一。文章里的 JavaScript/TypeScript/HTML/CSS 代码块可以直接运行!用的是 CodeSandbox 的 Sandpack 组件,懒加载的,不影响页面性能。
OG 图片自动生成。 分享文章到社交媒体的时候,会自动生成一张好看的封面图。这个是构建时生成的,不需要运行时渲染。
Service Worker + 离线支持。 加了 Service Worker 做缓存,断网了也能看之前访问过的页面。顺便还做了一个友链申请表单。
键盘快捷键 + Changelog + Snippets 筛选。 按 ? 可以查看所有快捷键,加了一个 Changelog 页面记录版本更新,Snippets 页面支持按标签筛选了。
这些功能加起来,提交了十几个 commit。每次加完一个功能都要跑一遍 pnpm build,确保静态导出没问题。Cloudflare Pages 是纯静态部署,任何需要服务端的功能都不能用,这个限制反而让我学会了很多静态站点的技巧。
代码规范化重构
功能加多了之后,项目结构开始变得混乱。CSS 文件散落在各处,图片目录没有统一规范,有些文件放错了位置。
我花了两天时间做了一次大重构:
- 把
css/目录统一改名为styles/ - 图片目录按用途分类:
covers/(封面)、banners/(横幅)、posts/(文章配图) - 移除了没用的测试文件和示例文件
- 把文档统一放到
docs/目录
重构的过程很枯燥,但做完之后整个项目清爽了很多。以后找文件也方便了。
中文标签 404 修复
这个 bug 困扰了我好一阵子。博客的标签系统支持中文标签(比如「技术」「前端」),在本地开发的时候一切正常,但部署到 Cloudflare Pages 之后,点击中文标签就 404。
排查了半天,发现是 encodeURI 的问题。之前代码里对标签做了 URL 编码,但 Cloudflare Pages 的静态文件路径不需要编码。移除了多余的 encodeURI 调用就好了。
顺便还做了一个标签显示名映射(tag-names.json),这样标签页上能显示原始的中文名称,而不是 URL 编码后的乱码。
液态玻璃 UI 改造
这是最大的一次改动。
起因是我看到了 Apple 的液态玻璃(Liquid Glass)设计风格,觉得特别好看。之前博客用的是「黑曜石玻璃」风格——深空灰背景加噪点纹理,虽然也不错,但看久了觉得有点沉闷。
我决定把整个博客改成液态玻璃风格。
一开始我想用 liquid-glass-react 这个 npm 包,但研究了一下发现它已经 10 个月没更新了,性能也不太好,还没有可访问性支持。最后决定自己用纯 CSS 实现。
液态玻璃的核心就是 backdrop-filter: blur() 加上半透明背景。我设计了 4 个层级:
/* standard - 普通组件 */
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px) saturate(1.8);
/* elevated - 突出组件(导航栏、卡片) */
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(24px) saturate(2);
/* subtle - 低调组件(标签、徽章) */
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(12px);
/* input - 输入框 */
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(16px);背景色从深空灰改成了天蓝色 #bfdbfe(浅色模式)和 #1f1f1f(深色模式)。
改造过程中踩了不少坑。一开始用了 SVG 滤镜做扭曲效果,结果在某些浏览器上会出现奇怪的变形和闪烁。后来去掉了 SVG 扭曲,只保留了 CSS 的 blur 和 saturate,效果反而更好。
还有一个问题是白色边框。液态玻璃组件之前有白色边框和阴影,在天蓝色背景上看起来很突兀。我把所有组件的白色边框和阴影都去掉了,改用更微妙的透明度变化来区分层次。
最后还要把 6 个代码块相关的 MDX 组件也适配液态玻璃风格,确保代码块在新设计系统下看起来协调。
整个改造涉及了 26 个组件,提交了好几个 commit。虽然工作量很大,但最终效果我很满意。
滑动 Pill 导航指示器
液态玻璃改造完之后,我觉得导航栏还可以更酷一点。
于是加了一个滑动的玻璃药丸(Pill)指示器。当你切换导航项的时候,一个半透明的玻璃药丸会平滑地滑动到当前选中的位置。动画用了 spring easing(弹簧缓动),有一种很自然的弹性感。
这个效果实现起来不难,但调参数花了不少时间。弹簧的刚度、阻尼、质量都会影响动画的感觉,太硬了像机械,太软了像果冻。最后调到一个我觉得刚好的状态。
kbar 搜索位移修复
这个 bug 是最后修的,也是最烦人的。
博客用了 kbar 做搜索(按 Ctrl+K 打开)。kbar 打开的时候会给 body 加 overflow: hidden,防止背景滚动。但这样做有个副作用:滚动条消失了,页面内容会往右移动一点点(大概 15-17 像素),看起来就像页面「抖」了一下。
我前前后后试了好几种方案:
- 用
padding-right补偿滚动条宽度 —— 不行,因为 sticky header 的居中计算会受影响 - 动态测量滚动条宽度再补偿 —— 太复杂,而且不同浏览器滚动条宽度不一样
- 最终方案:直接用 CSS 覆盖 kbar 的行为
body[style*='overflow: hidden'] {
overflow-y: scroll !important;
}简单粗暴但有效。让 body 始终保持 overflow-y: scroll,滚动条一直在,就不会有位移了。kbar 的遮罩层本身就会阻止用户滚动,所以不影响功能。
有时候最简单的方案就是最好的方案。
写在最后
从开始搭建到现在,已经过去好几天了。特别是今天(2月15日),我花了一整天时间优化深色模式的渲染问题。这个过程中遇到了很多挫折,有时候真的很想放弃。但每次解决一个问题,那种成就感是无法形容的。
作为一个高中生,这是我第一次完整地做一个项目。虽然代码写得不够好,但我学到了很多:
- 学会了如何阅读文档和源码
- 学会了如何调试和定位问题
- 学会了如何系统地优化性能
- 学会了如何坚持下去
最重要的是,我有了一个属于自己的博客,可以记录学习和成长。
如果你也想做一个博客,不要害怕,大胆去试!遇到问题很正常,解决问题的过程就是成长的过程。
后来我把浏览量统计功能去掉了,博客现在是一个纯静态站点,部署在 Cloudflare Pages 上,简单又稳定。
希望这篇文章能帮到和我一样的初学者。如果你有问题,欢迎交流!
相关资源
- 我的博客:https://blog.the37777777.top
- 原项目地址:https://github.com/mk965/mengke.me
- Next.js 官方文档:https://nextjs.org/docs
- Cloudflare Pages 文档:https://developers.cloudflare.com/pages/
下一步计划
- ✅ 添加了 Giscus 评论系统
- ✅ 做了 TimeMark 生日提醒系统(我的第一个 Docker 项目!)
- ✅ 添加了 11 个新功能(进度条、系列导航、Sandpack、OG 图片等)
- ✅ 完成了液态玻璃 UI 改造
- ✅ 修复了中文标签 404 问题
- ✅ 代码规范化重构
- ✅ 完成 v2.0 全站增强(50+ 项改进,15 个 MDX 组件,Pagefind 搜索)
- 📝 持续写文章,记录学习
- 🎨 继续打磨液态玻璃细节
- 🚀 探索更多有趣的项目
加油!💪


