从零到一:我的个人博客开发历程

吾生也有涯,而知也无涯。—— 庄子


前言

作为一个业余前端开发者,搭建一个属于自己的博客系统,一直是心中的执念。不是没有现成的方案——WordPress、Hexo、Hugo 一抓一大把,但总觉得少了点什么。少了那种从零开始、一行一行敲出来、每一个像素都由自己掌控的感觉。

于是,我开始了这段漫长的博客开发之路。


一、起点:选择 ThriveX

最初接触的是 ThriveX,一个现代化的博客管理系统。它提供了后端 API 和管理后台,前端可以自由发挥。这正是我想要的——不需要自己写后端,但前端完全可控。

技术栈很快确定下来:

  • 框架: Next.js 16(App Router)
  • UI: React 19 + Tailwind CSS v4
  • 动画: GSAP
  • 图片优化: sharp + next/image
  • 部署: Vercel / Docker

目标很明确:做一个有自己风格的博客,不是千篇一律的主题模板。


二、设计风格:水墨与赛博的碰撞

我不想做又一个极简白底博客,也不想做暗黑科技风。最终选择了一个比较小众的方向——水墨赛博

  • 字体用的是「Noto Serif SC」,自带书卷气
  • 色彩以深色为底,点缀铜金和青碧
  • 首页有一个水墨粒子动画的 Hero 区域
  • 页脚有一只墨笔画的小动物,跟着页面 sticky 在底部

这个风格说不上惊艳,但至少有辨识度。


三、核心页面的开发

3.1 首页

首页是博客的门面,花了不少心思:

  • Hero 区域: 全屏水墨风,「墨·Loong」的大字标题,下方是粒子动画
  • 轮播图: 展示置顶或推荐文章
  • 精选文章: 从后端 /api/article/hot 获取热门文章,3 列卡片布局
  • 最新文章: 分页加载,带左右滑动翻页动画
  • 时间线: 文章的时间轴展示
  • 最新评论: 展示访客的评论

每个区域都用了 Suspense 做流式加载,首屏不会被一个慢接口拖住。

3.2 文章详情页

这是博客最核心的页面:

  • 全屏封面 Hero: 文章封面铺满整个首屏,标题和元信息浮在上面
  • 目录侧边栏: 自动解析 h2/h3 生成目录,滚动时高亮当前章节
  • 阅读进度条: 顶部的渐变进度条,实时显示阅读位置
  • Markdown 渲染: 支持 GFM、数学公式、代码高亮
  • 评论系统: 支持嵌套回复,QQ 邮箱自动获取头像
  • 上下篇导航: 文章底部的前后翻页

3.3 其他页面

  • 标签云: 标签按文章数量缩放大小,点击跳转到该标签的文章列表
  • 数据总览: 文章、标签、友链、评论的统计面板
  • 设备清单: 展示我的设备,支持图片灯箱放大
  • 足迹地图: 高德地图标记我去过的地方
  • 友链: 分组展示友情链接
  • 说说: 类似朋友圈的时间轴动态
  • 留言墙: 访客留言板
  • 鱼塘: RSS 订阅源聚合

四、踩过的坑

4.1 Lenis 平滑滚动的噩梦

最初引入了 Lenis 库做平滑滚动,效果确实丝滑。但上线后频繁出现一个问题:页面偶尔会卡死,无法滚动

排查了很久,发现是 Lenis 用 CSS transform 模拟滚动,和浏览器原生的 scrollTop 会产生位置不同步。当两者冲突时,滚轮事件被 Lenis 拦截但不执行,页面就卡住了。

最终的解决方案:移除 Lenis,用 GSAP 的 ScrollToPlugin 替代。GSAP 内部用原生 window.scrollTo,不会出现位置不同步的问题,而且 autoKill: true 可以在用户手动滚动时自动取消动画。

4.2 Emoji 图标的渲染问题

博客里的分类和标签名称带了 emoji(比如 🗂️ 默认分类),但前端用 emoji 做功能图标时,在某些设备上渲染不一致。

解决方案:把所有功能图标替换为 SVG,emoji 只保留在内容数据中。

4.3 SEO 的 Canonical URL

一开始所有页面的 canonical 都指向根域名 https://loongblog.fun,导致搜索引擎认为所有页面都是同一个内容。

修复方式:在 layout 中移除全局 canonical,给每个页面单独设置。动态页面(文章、标签、分类)在 generateMetadata 中根据参数生成。

4.4 图片优化

最初全部用原生 <img> 标签,没有 WebP/AVIF 转换,也没有响应式尺寸。后来把服务端组件中的图片迁移到 next/image,配合 sharp 自动优化。

但对于外部 API 返回的图片(友链头像、鱼塘 RSS 图片),因为域名不可控,无法加入 remotePatterns,只能保留 <img> + loading="lazy"


五、性能优化

5.1 首页流式加载

把首页拆分成 5 个独立的 Section,每个用 Suspense 包裹。用户先看到 Hero,其他区域异步加载,不会被最慢的接口拖住。

5.2 字体优化

Noto Serif SC 的完整字体文件有几 MB,用 next/font/local 做子集化后,只剩下几十 KB 的 woff2 文件,按需加载。

5.3 图片懒加载

所有非首屏图片都加了 loading="lazy",设备页、足迹页、友链页的图片不会在首屏加载。

5.4 缓存策略

  • 静态页面:revalidate = 300(5 分钟 ISR)
  • 文章详情:revalidate = 60(1 分钟 ISR)
  • 标签/分类列表:revalidate = 300

六、安全加固

  • 安全头: X-Content-Type-Options、X-Frame-Options、Referrer-Policy、HSTS
  • API 隐藏: robots.txt 禁止爬取 /api/ 路径
  • XSS 防护: Markdown 渲染用白名单过滤标签
  • 环境变量: .env 文件不提交,敏感配置通过环境变量注入

七、从本地到线上

部署用的是 Docker + Vercel 混合方案:

  • 前端 Next.js 部署在 Vercel,自动 CI/CD
  • 后端 ThriveX Admin 部署在自己的服务器
  • 图片存储用七牛云 OSS
  • CDN 用 Cloudflare

域名 loongblog.fun,ICP 备案已完成。


八、回头看

这个博客从第一次 commit 到现在,经历了无数次重构。滚动问题改了五六版,SEO 优化做了两轮,图标替换了一批又一批。有时候改一行代码要推三次才能上线。

但每次看到自己的博客在浏览器里打开,那种满足感是用任何现成模板都给不了的。

技术栈会过时,代码会被重构,但这个过程本身,就是最好的学习。


技术栈一览

类别技术
框架Next.js 16 (App Router)
UIReact 19 + Tailwind CSS v4
动画GSAP (ScrollToPlugin, ScrollTrigger)
图标自定义 SVG
图片next/image + sharp
Markdownreact-markdown + remark-gfm + rehype-katex
状态管理Zustand
地图高德地图 JS API
部署Vercel + Docker
CDNCloudflare
图床七牛云 OSS

写于 2026 年 6 月,Loong·Blog