上一次写博客搭建,还是 Hexo 和 Butterfly。那时候主要在解决:网站怎么跑起来,文章怎么发出去,换台电脑以后怎么办。 关于主题什么的,找一个现成可用的搭起来已经焦头烂额了。 这次终于又回来折腾了,而且一不小心,把整个博客重新装修了一遍。
那么就接着记一下这次的搭建过程吧~
为什么又重新搭了一遍
一开始搭这个博客,是因为程序员刻板印象之——人手都有自己的一个博客,很酷。而作为一个与视觉,交互等天天打交道的前端程序员,一个酷炫的个人博客就更重要了。我作为一个半路出家的FE,出于皈依者狂热以及曾经的文青病,有一个自己的博客一直是我的梦想。
于是在深圳虾皮实习时,我开始捣鼓这个博客。当时AI还不是很发达(2025年8月),agent和浏览器不连通,我只能查方案然后再自己动手做。
最开始用 Hexo,是因为它上手方便,配上 Butterfly,改改配置就能得到一个很漂亮的博客。但是butterfly的bug实在有点多了,搭建好后一位朋友点进来,本来想帮我“测试”一下稳定性,结果就手动点了两下,访问量一下变得巨大无比(估计是往下跌最后反向了)。
Butterfly对我来说也太花里胡哨了。我放了一些我喜欢的二次元图片进去,试图做得神秘一点,结果搞的好像个老肥宅的冰丝痛衣一样。这种往yml里填空的形式让我觉得拘束,也跟我的审美严重打架了。
另外就是除了技术文章,还有书、电影、音乐剧、歌单、动画、旅行,以及那些和星空有关的记忆,我都想逐一展现到博客中。它们并不都适合写成一篇篇文章,再按发布日期排成列表。比如喜欢的音乐,我更想让歌曲围在音乐人名字旁边;一次旅行,也可能由好几条不同时刻发的朋友圈组成。
还有一个严重挫伤我更新热情的设置:Butterfly的更新是古法手动更新。写一篇文章之后,我必须手动上传到github,然后手动执行更新。而且它的数据库和配置等等要装在一台指定的服务器上,对我而言,那就是我的电脑。我一开始是部署在工作电脑上,我离职前又费了很大力气搬迁到了我的个人电脑上。再入职时,我实在是没力气再搬来搬去了。
在虾皮的最后时光,我写了一些技术文章。因为更新复杂的问题,我改为在notion上存放这些文章。notion采用的是非常简洁的方式,以维护表格/数据源的方式来同步更新,以一种画廊的形式进行展览。但是文章外的很多东西,那些多模态的,更加私人的东西,就不好展示了。
我想要有一个符合自己审美的,非常定制化的网站。
正式入职bytedance之后,AI技术迎来了新的飞跃——gpt 5.6 claude code等横空出世,更有codex大杀器。于是我开始用内部的agent工具AIME从0开始搭这个新博客。AIME有内置的浏览器,内置的llm很聪明,改的也挺快的,慢慢我就完成了第一版。不过,AIME是异步工程的方式,而且每次都需要部署后才能走查并进行下一轮修改,虽然是内置的项目,但还是太慢了,特别是对一些细小但需要修改多轮的样式上。
gpt 6 astra 降世后,我开始付费上班,用上满血版codex了。这下可真是如虎添翼,超高级的反应速度,超级棒的审美,超级高效的理解能力,更有各种skill和CLI和外界连通…… 基本上我只需要专注我的设计和创作就行,其他的各种琐碎事物,都可交予codex进行。我个人又有一点技术背景,对于技术的选择和优化上具备一些判断力。于是在中秋前这段不忙的日子里,在摸鱼时光中 新博客的完成度火速推进,很快便迎来了第一版发布。
在这一次的搭建中,有原先的基建,又有AI辅助,终于不再束手束脚,可以尽情地发挥我的想象,贯穿我的美学了。
🌟 这次的技术方案
这次从 Hexo 迁到了 Astro。域名没有换,网站仍然托管在 GitHub Pages 上。
目前这套组合是:
| 部分 | 现在使用的方案 |
|---|---|
| 页面与组件 | Astro |
| 文章正文 | Markdown + Content Collection |
| 书影音等收藏 | 本地结构化数据 |
| 游记 | JSON 数据 + 本地图片 |
| 构建与包管理 | Node.js 24 + pnpm |
| 发布 | GitHub Actions → GitHub Pages |
| 评论服务 | Waline + Vercel + Neon |
这里说的“静态站点”,主要指文章、收藏和游记这些内容在构建时生成。评论和星光计数仍然需要向后端请求,并不是把整个网站都塞进数据库。
我这次比较喜欢的一点是:内容和展示可以分开整理。文章正文放 Markdown,歌曲、作品和旅行信息放数据文件,页面组件负责把它们摆出来。以后加一部动画或者一篇游记,不需要再复制一整页代码。
本地启动也很简单:
pnpm install --frozen-lockfile
pnpm run dev
开发预览默认在 http://localhost:4321/。准备发布前,再跑一次:
pnpm run build
需要注意本地 Node 版本和线上保持一致。这次使用的是 Node 24。
先把内容放回来
旧文章不能只搬过来就算了
这次有一部分文章是之前搬运过来的。内容相同,但不同的平台格式不同,直接搬运会有很多兼容问题:多余的换行、错位的列表、代码块和正文混在一起,还有看起来像链接、点进去却不对的东西。
所以最先做的是把已有文章重新梳理一遍,再统一文章页的标题、标签、正文和卡片样式。
迁移以后,文章路径统一成了:
/posts/<slug>/
这样名字比较清楚,也方便从书架、影评或者逐星记里直接关联到正文。不过,新路径能访问,不等于所有旧分享链接都已经兼容。旧站历史路由仍然需要逐步检查,不能只看首页正常就认为迁移完成了。
我的收藏罐
收藏罐大概是这次最容易越做越多的地方。
- 书架微光:有札记的书可以进入正文,没有写评论的书也可以留在主题书架上。同一个系列不必按第几本反复罗列。
- 银幕潮汐:把自己的影评全文收录进来,阅读时不需要再跳回豆瓣。
- 夜航歌单:用主题切换和音乐人星图整理喜欢的歌,避免所有列表一次铺满页面。由于很多歌曲需要会员才能听,目前采用的方案是将免费歌曲的链接放入,可以直接点击播放。
- 次元回响:放那些喜欢、也影响过自己的动画,后来又把卡片里的装饰图换成了真实的作品封面。
书架没有必要装成读书数据库,歌单也不一定要严格按流派归类。我更在意的是:回头看的时候,能不能认出“这是我喜欢的那些东西”。
对于还没想好、也没有内容的入口,比如有趣的网站,先不展示。完美主义是不需要的,要允许内容有慢慢生长出来的时间。
给整个网站找一套共同的样子
这次花了不少时间在一些看起来很小的事情上:标题、英文副标题、导航节点、卡片边框、图案,以及各种星星。
我的设想是,这个博客是以星空和银河为主题,有较为神秘美丽的基调:背景是永夜的,辅以紫和蓝,以及濛濛星光,少量青色与金色如同流星般点缀其中,线条插画是塔罗般简约的。后来把它们逐渐用到了首页、关于页、文章、收藏馆和游记上,才慢慢有了整个网站是一体的感觉。
最先应用上的是逐星记板块。在这里,我确定了基本基调,也最先装填好了内容。我都是先和AI聊好聊透彻,再让它动手去做。
在Logo上也做了一些小巧思:
首页中间那颗大星星则做成了立体旋转的效果。实际使用的是几何计算配合 SVG,把立体星体的面投影到二维页面上;轨道上的小行星也会沿着轨迹移动。没有为这个图案再引入一整套三维引擎。
逐星记:一条记忆里的河
逐星记是第一个生长出来的页面,也是我花费心思最多的一个部分。
它承载的东西非常私人,以我和星空的故事为纽带,贯穿了我的成长史:少年时代仰望星空,后来落回人间;见过宇宙的辽阔,也在山海、工程和现实生活里慢慢重新认识自己。
时间轴被做成了一条纵向的航道。节点按记忆的温度分成冬夜、夏夜、钢灰和暗紫,小船随着阅读进度往前走,经过不同的阶段时,也会被那一段记忆染色。
节点配图用了插画感的表现,顶部星穹则保留了和首页相近的四芒星。门一层层往里,小船一段段往前,希望读起来有一点翻开旧手札的感觉。
旅途拾光:从朋友圈开始
原来这里计划放照片墙,但真要开始整理,问题就来了:我是个非常喜欢拍照和收藏图片的人,手机中的图片浩如烟海,挑选已是难事,不整理就放上来还会显得杂乱。
所以这次试了一种更顺手的方式:先把已经写过的旅行朋友圈收进来。我的朋友圈内容,每次旅行我都会认真写一个总结。
素材入口用的是飞书多维表格。一条朋友圈对应一条记录,填旅行名称、日期、地点、原文,再上传照片或拼图。同一次旅行的几则记忆,用相同的旅行名称归在一起。
实际试下来,PC 端复制和上传比较方便。代价是有些图片清晰度不如原图,但对于先把记忆留下来这件事,它降低了不少阻力。
整理后的流程是:
朋友圈文字与图片
↓
飞书素材表
↓ 手动整理、检查
本地游记数据与图片
↓ 预览确认
正式游记页面
这里目前没有自动同步,也没有填完表就自动发布。仍然是先收集一批,再整理、预览,最后确认发布。
页面上保留朋友圈的原文和图片顺序,长拼图可以点开看。同一次旅行如果有多则记忆,折叠时先展示第一则,想继续读再展开。原来的“照片墙”也就变成了现在的“旅途拾光”。
互动:星间来信,和一颗可以点亮的小星星
只有内容的话,网站多少还是有一点安静。后来就开始想,朋友路过这里,除了读文章,是不是也能留下点什么。
最后分成了两种互动:
轻一点的,是点亮小星星。 不必先想好一段话,留下星光就可以。小星星在侧边陪伴,可以拖动,点亮前半透明,点亮后更明亮;附近会短暂出现“收到了你的星光”。手机端则用双击来区分点亮和拖动。
想说点什么的时候,可以留言。 全站的留言放在“星间来信”,文章和游记下面也有各自的评论区。进入具体文章后,从小星星上的留言入口可以移动到正文底部,并聚焦输入框。
后端用 Waline,部署在 Vercel,数据保存在 Neon。网站本身继续放在 GitHub Pages,评论服务单独运行。
有几个地方值得记一下:
- 不同文章和游记需要使用各自的路径作为评论标识,避免串到同一个评论区。
- 站长可以在 Waline 管理后台处理评论,也可以用管理员身份回复。
- 邮件通知需要另外配置发信服务。这次配好后也实际收到了测试邮件。
- 发信凭据和数据库连接信息放在服务端环境变量里,不应该写进前端代码;排查日志时也要留意是否输出了敏感配置。
手机端还暴露了一些桌面上不明显的问题,比如按钮挤在一起、字数统计换行,以及小星星挡住评论操作区。还是得用手机实际看一遍,桌面缩小窗口不能替代所有检查。
🎵 歌单真的能在博客里播放吗
最初只是想给歌名加上网易云链接,后来又觉得:既然已经在这里了,能不能顺手听一下?
能,但不是所有歌都能。
搜索能找到歌曲,不代表匿名访问时有完整音源。会员限制、不同录音版本和音源可用性,都要另外核对。不能只根据歌名搜到的第一个结果就接上去,同名翻唱和混音尤其容易弄错。
这次检查了站内的 87 首不同曲目。截至这次核验,32 首通过了公开音源检查,并在页面里成功启动播放;53 首没有取得匿名完整音源,另有 2 首无法确认对应版本。
于是采用了一个简单的规则:
能确认播放的,歌名旁边放一个小音符;不能播放或还没确认的,就继续作为普通文字展示。
没有额外做账号登录,也不代理会员歌曲,不把音频下载下来重新托管。播放器只在点击后请求音源,页面加载时不会把整个歌单逐首探测一遍。
播放器本身也比较轻:浏览器原生音频控件,外面包一层半透明的夜色毛玻璃,配一点星辉和小星星。可以播放、暂停、拖动进度、切歌和关闭。
不过,“这次检查可以播”不是永久保证。版权或网络变化后,歌曲仍可能失效,所以也做了失败提示,而不是让按钮一直转圈。
从无缝播放,到先让页面好好打开
中间试过一版跨页播放:把播放器放进全站布局,用 Astro 的 ClientRouter 接管站内导航,再用 transition:persist 保留音频元素。切页时音乐不断,确实很舒服。
但朋友们用手机试过以后,又遇到了另一个问题:首屏已经能看了,点击导航却长时间停在“正在前往”。电脑上顺畅,不代表大陆移动网络下也一样顺畅。
排查时才发现,无刷新切页并不是点击后立刻把新页面放出来。它要先拿到目标页面的 HTML,再等待需要的新样式,最后才替换当前内容。不过这次检查发现,从首页去文章、关于等常用页面并没有额外的样式文件,不能简单把慢归咎于 CSS,也没有证据说必须等首页全部加载完才能发起跳转。
加过即时切页反馈、慢请求提示,也给视口里的常用导航做了适量预加载。提示能让人知道点击生效了,却不能让卡住的请求自己变快。
于是最终先退回普通整页跳转,让浏览器直接处理导航,保留预加载,去掉自定义的切页等待界面。代价是跳转或刷新后音乐会停止,当前页面里的轻播放器仍然保留。以后如果还想边听边逛,可以考虑单独开一个“随身听”页面;目前还没有做。
这次算是一个小小的取舍:无缝播放很讨喜,但朋友能顺利走进每个页面,对这个博客更重要。普通导航也不能消除网络延迟,改完后仍然要在真实手机网络里继续观察。
发布之后,才看见真实的网络
本地预览和电脑上的体验一度都很好,发给朋友才发现:大陆手机网络下,点亮星星、加载留言、发送评论都可能很慢,甚至失败。换成国外网络又快了很多。
我们用电信热点拆开测了连接与响应耗时。旧的评论服务默认域名在当时的直连测试里多次连接超时,而接到自己的 comments.starrydome.top 后,同一批接口能够正常返回,留言和点亮的实际体验也改善了不少。这次并没有把函数和数据库迁到新加坡,改的是评论服务的访问域名。一次测试不能保证所有运营商永远畅通,但至少比凭感觉搬机房更有依据。
前端也做了一些调整:
- 点亮星星先给出动画与文字反馈,再异步发送数据,不让手指等着后端返回。视觉点亮和服务端保存是两件事,不能把即时反馈当成已经入库。
- 流星数量有成功读取后的本地缓存,再次访问先显示已有数字,后台更新。第一次没有缓存时,用自然的文案承接,不编一个数字,也不一直挂着省略号。
- 留言区域保留加载占位,部分交互提前到 DOM 就绪后初始化,减少对整页资源加载完成的等待。
还有一个完全不涉及技术栈的问题:朋友说“星间来信”太难找了。原来主要靠小星星和页脚进入,后来又试了放进导航末尾,最后把它独立放在站点标题旁边、导航上方,前面带一枚简单的四芒星。让愿意留言的人先找到门,本身也是交互的一部分。
以后怎么写、怎么发布
现在写原创文章,可以直接在 src/content/posts/ 下新建 Markdown 文件。文件头大致如下:
---
title: "文章标题"
date: 2026-09-22
category: "技术札记"
tags:
- "博客"
slug: my-new-post
---
下面就是正文。分类目前有“技术札记”“生活切片”和“拾光摘录”;slug 决定访问地址。迁移文章可以记录来源,原创文章则不需要填写 sourceDoc。
文章图片可以放在 public/article-media/<slug>/ 下,再从站点根路径引用:

这里和第一篇里写的 Hexo 要区分一下:当前 Astro 项目的 public/ 是静态资源目录,不是可以随手清掉的构建缓存。public/CNAME 里还保存着自定义域名配置,也要一起保留。
另外,上一篇里的 Hexo 草稿方法不能直接套用过来。当前文章集合还没有实现按 draft 或未来日期过滤的逻辑,放进正文目录的文章会参与构建。没写完的文章,不能只加一个 draft: true 就以为它不会上线;发布前仍然要确认本次包含哪些内容。
发布也从原来的 hexo g / s / d,变成了:本地预览和构建检查 → 提交源代码 → 推送 master → GitHub Actions 构建并发布。现在有了类似Codex的工具,推送更新就方便多了。
现在仓库里保存的就是源文件,不再沿用之前“源码仓库一份、生成文件仓库一份”的那套操作。换设备时,拉取这个仓库,安装对应版本的 Node 和依赖,就可以继续维护。
这几轮调整也跑过自动化测试和生产构建。但构建成功也不等于所有体验都已经验收:正式站的访问、图片加载、手机交互和外部音源,仍然需要实际检查。
和 Agent 一起折腾的过程
这次很多实现是和 Codex 一边讨论、一边在本地预览里改出来的。
我觉得最有用的方式,是一次推进一个具体的东西。比如先把书架做出来,再看卡片;先把小星星的样子和交互确定,再接后端;音乐也先拿几首歌试,确认可行后再检查完整歌单。
有些需求一开始很难讲清楚,但看到页面就会知道“不太对”:太挤了、字太小了、又和博客整体美学不像一套了。把这些直观感受说出来,再一点点调整,往往比最开始写一大份完美需求更有用。
当然,内容还是要自己来决定。哪些作品值得留下,旅行原文怎么写,什么话真的像自己,这些不能交给一个漂亮模板代劳。
这篇也先记到这里。接下来基本上就是常规装修,以及内容的写作了。终于新房入伙,可以开始主线任务啦(笑)。
这个系列的前两篇
- 博客搭建(一) 初始化:域名、Hexo、Butterfly,以及最初的部署过程。
- 博客搭建(二) 草稿和多设备管理:旧版 Hexo 博客的草稿与多设备管理方式。
前两篇保留当时的搭建记录,涉及 Hexo 的命令和目录约定,与现在这版 Astro 网站有所不同。