与 AI 合伙人相爱相杀 vlog 7:404 消失的那天
周日的回访,老大发现网站 404 问题,三个梯队排优先级,404 歼灭战,TOC 初体验,导航闭环完成。

序章:发现 404 问题
"滴——"
咖啡机发出清脆的声响,一杯浓缩美式刚好落进杯子里。老大站在厨房吧台前,目光却盯着客厅餐桌上的 MacBook——浏览器开着,页面停留在博客首页。
三月的第一个周日,空气中有周末特有的松弛感。老大本来想今天休息一下:看看刚买的《AI 未来简史》,听两张黑胶唱片,晚饭做个红烧排骨。但昨天写完 Vlog 6 之后,他总觉得有什么事情没做完,心里像悬着个东西。
"先看看网站吧。"老大心里想着,端起咖啡杯走向客厅。
他在沙发上坐下,MacBook 的屏幕还亮着。首页的发光卡片很好看,"查看全部"按钮就在那儿,蓝绿色的,在深色背景下很显眼。老大想都没想就点了下去——
404 Not Found
白色的页面,黑色的文字,没有任何装饰,像一张面无表情的脸。
老大的眉头一下子皱了起来。他放下咖啡杯,在 Claude Code 终端里输入:
收到,老大。我检查一下路由结构……
确认了,三个页面都不存在:
/blog - 文章列表页(不存在)
/projects - 项目展示页(不存在)
/about - 关于页面(不存在)
首页"查看全部"按钮指向 /blog,所以也会 404。
另外,文章详情页的"返回内容列表"按钮指向首页,应该指向 /blog 才对。
老大靠在沙发上,目光投向窗外。楼下花园里,几个孩子正在骑自行车,笑声隐约传来。本来想休息的,看来今天又有事干了。
"这也太显眼了。"老大心里琢磨着,"用户一进来就点 404,这体验太差了。"

第一章:三个梯队排优先级
老大把 Macbook 移到书房,在台式机前坐下。机械键盘发出清脆的声响,终端已经打开,光标在那里闪烁,像一只等待指令的眼睛。
收到,老大。我整理一下当前可选的开发任务:
技术开发类(排除内容创作):
🔴 第一梯队(紧急 - 修复 404):
创建 /blog 文章列表页
修复文章详情页"返回内容列表"按钮
🟡 第二梯队(重要 - 完善网站):
创建 /about 关于页面
创建 /projects 项目展示页
🟢 第三梯队(体验优化):
TOC 文章侧边导航功能
Vlog 对话气泡样式优化
代码高亮与排版
建议执行顺序:第一梯队 → 第二梯队 → 第三梯队(TOC)。
理由:先解决最显眼的 404 问题,用户体验影响最大。
老大摸着下巴,三个梯队在他脑子里过了一遍——第一梯队和第二梯队都是静态页面,确实很快,加起来一个半小时就能搞定。然后剩下的时间可以集中精力搞 TOC,那个功能比较复杂。
"对了,TOC 是什么来着?"老大突然想到,"之前好像提过,但具体怎么实现还没搞明白。"
收到,老大。这个思路也可以。
那我先跟你解释一下:TOC 是什么?
TOC = Table of Contents,也就是文章目录。
作用:
长文阅读时,右边显示一个跟随滚动的目录
点击目录项可以快速跳转到对应章节
滚动时自动高亮当前阅读的章节
显示层级:通常显示 H2(二级标题)和 H3(三级标题)
显示位置:通常放在右侧边栏,不干扰阅读
两种主流技术方案:
方案 A(服务端预提取):
优点:首屏快,SEO 友好
缺点:需要修改 Markdown 渲染逻辑,复杂度较高
方案 B(客户端 DOM 解析):
优点:简单直接,不需要动服务端逻辑,改动风险小
缺点:首屏渲染后才会显示目录(几百毫秒延迟,用户基本感知不到)
我的建议:方案 B,简单直接,适合我们这种场景。
老大端起咖啡杯喝了一口,温度刚刚好,舒服了。

第二章:404 歼灭战
老大站起身,走到客厅,打开电视——刚好是体育新闻,正在播放篮球比赛。他靠在沙发上,一边看比赛,一边等巴基搞定。
收到,老大。
第一梯队任务:
创建 /blog 文章列表页(与首页风格一致,展示全部文章)
修复文章详情页"返回内容列表"按钮(改为指向 /blog,文字改为"返回文章列表")
预计时间:30 分钟。
你去忙吧,有需要决策的地方叫你。
电视里,球员在球场上奔跑,篮球在篮筐上弹跳,发出"砰、砰"的声响。老大靠在沙发上,看着比赛,时不时喝一口咖啡。
大概过了十五分钟,终端的声音从书房传出来。
老大,第一梯队搞定了。
已完成:
/blog 文章列表页创建完成
文章详情页"返回文章列表"按钮已修复
要继续搞第二梯队吗?
老大从沙发上坐起来,关掉电视,走回书房。篮球比赛的比分停留在 87:82,还行。
收到,老大。
第二梯队任务:
创建 /about 关于页面(博主介绍、技术栈展示、联系方式)
创建 /projects 项目展示页(展示当前项目,预留扩展)
预计时间:1 小时。
你去忙吧。
老大点点头,又走到客厅——电视已经关了,他拿起茶几上的《AI 未来简史》,继续看刚才没看完的章节。
阳光慢慢移动,客厅里的光影也在慢慢变化。老大坐在沙发上,一页一页翻着书,偶尔看一眼书房方向。
大概过了四十分钟,终端的声音又传出来。
老大,第二梯队搞定了。
已完成:
/about 关于页面创建完成
/projects 项目展示页创建完成
第一梯队 + 第二梯队全部搞定!
启动开发服务器看看效果?
老大放下书,走回书房。书已经看了六十多页,不错,今天的阅读任务也完成了。
开发服务器启动,浏览器打开 http://localhost:3000。老大依次点开:
/blog—— 文章列表页,完美/about—— 关于页面,完美/projects—— 项目展示页,完美- 文章详情页的"返回文章列表"按钮——完美
"404 问题终于解决了。"老大点点头,"现在搞第三梯队,TOC。"

第三章:TOC 初体验(上)
老大站起身,走到书房角落的健身自行车旁。那是一辆折叠式的动感单车,平时不用的时候可以收起来,不占地方。
今天的健身任务还没完成,正好可以一边健身一边让巴基写代码。
他把手机架在车把的手机架上,又把《AI 未来简史》翻开,调整角度放在旁边。然后骑上车,调整好阻力,开始慢慢蹬起来。车轮转动,发出轻微的嗡嗡声。
收到,老大。这个主意好。
我先创建 TOC 组件目录和类型定义,有需要决策的地方叫你。
老大一边蹬车,一边看书。阳光从百叶窗照进来,在地板上投下条纹状的光影。书房里很安静,只有健身车的嗡嗡声、台式机风扇的转动声,还有窗外偶尔传来的鸟鸣。
大概过了二十分钟,手机震了一下。
老大,打扰一下,有个决策需要你做。
TOC 组件的样式,你想要什么样的?
极简风格,纯文字,简单的高亮
玻璃拟态,半透明背景,发光边框,跟网站整体风格一致
其他风格
老大看着手机,手指在屏幕上轻点。
老大把目光移回书本,继续蹬车。聊天在后台运行,有新消息时手机会震动。
又过了大概十五分钟,手机震了一下。
老大,又有个决策。
目录显示层级,只显示 H2,还是 H2 + H3?
只显示 H2,简洁,目录不会太长
H2 + H3,详细,方便精准跳转
老大看着手机,手指在屏幕上轻点。
老大把目光移回书本,继续蹬车。阳光慢慢移动,地板上的光影也在慢慢变化。他已经看了快四十页,健身车的里程表显示已经骑了五公里。
又过了大概二十分钟,手机又震了一下,这次是连续好几条消息。
老大,基础组件开发完成。
已完成:
TOC 组件目录和类型定义
桌面端 TOC 组件,右侧边栏,玻璃拟态样式
移动端 TOC 组件,右下角悬浮按钮加抽屉模式
主 TOC 组件,整合桌面端和移动端
下一步,修改文章详情页集成 TOC,要继续吗?
老大慢慢停下健身车,车轮的嗡嗡声逐渐消失。他拿起毛巾擦了擦汗,额头上、脖子上都有微微的汗。里程表显示六公里,不错,今天的健身任务完成了。
他拿起手机,从车把上取下,走到书桌前。

第四章:TOC 初体验(下)
老大在书房里走来走去,活动着刚才健身有些僵硬的肩膀。巴基在后台修改文章详情页,集成 TOC 组件,终端的代码在屏幕上快速滚动。
大概过了十分钟,终端的声音停了。
老大,集成完成了。
启动开发服务器看看效果?
老大回到书桌前坐下,端起咖啡杯喝了一口。
开发服务器启动,浏览器打开 http://localhost:3000。老大点开一篇 Vlog 文章——
右侧边栏出现了,玻璃拟态样式,半透明背景,发光边框,跟网站整体风格一致。目录列出了所有 H2 和 H3 标题,H3 有缩进,区分层级。
往下滚动页面——
"不对啊。"老大皱起眉头,"目录怎么跟着一起往上走了?应该保持在同一个位置不变啊。"
收到,老大。我检查一下……
找到问题了。TOC 组件本身有 sticky top-24,但外层容器没有设置 self-start,导致元素拉伸到父容器高度,sticky 无法生效。
修复方案:
在外层容器添加 self-start
直接在 aside 标签上设置 sticky top-24 h-fit max-h-[calc(100vh-8rem)] overflow-y-auto
要修复吗?
老大摸着下巴,这个方案听起来靠谱。
老大站起身,走到窗边。楼下花园里,几个孩子正在追逐打闹,笑声隐约传来。
大概过了五分钟,终端的声音停了。
老大,修复完成。
刷新浏览器看看效果?
老大回到书桌前,刷新浏览器——
这次对了。往下滚动页面,目录保持在右侧边栏同一个位置不变,像悬浮在那里一样。
"完美。"老大点点头,但很快又发现了新问题——目录有点太挤了,宽度不够。
收到,老大。样式优化内容:
宽度增加到 w-96(384px)
内边距更宽松,p-7 替代 p-6
整体容器加宽到 max-w-7xl
正文最小宽度保护,xl:min-w-[700px]
要调整吗?
老大端起咖啡杯,转了转。
大概过了三分钟,终端的声音停了。
老大,样式优化完成。
刷新看看?
老大刷新浏览器——现在好看多了。目录宽度足够,不再拥挤,内边距宽松,整体视觉效果舒服多了。
他试着点击目录里的一个标题——页面跳转到对应章节,但位置有点偏高,标题被顶部导航栏挡住了。
收到,老大。修复方案:手动计算滚动位置,减去导航栏高度 100px。
要修复吗?
老大拿起鼠标,在鼠标垫上蹭了蹭。
老大站起身,走到厨房,给自己接了一杯水。饮水机发出咕嘟咕嘟的声响,水流进杯子里。他喝了一口,水温刚好。
回到书房的时候,巴基已经搞定了。
老大点击目录里的一个标题——这次对了,页面平滑滚动到对应章节,标题正好显示在导航栏下方。
但很快又发现了另一个小问题——点击第一个标题的时候,目录的选中状态不是第一个标题,是第二个。
收到,老大。问题原因:IntersectionObserver 在手动滚动期间仍在触发,覆盖了手动设置的选中状态。
修复方案:添加 isScrollingRef 标记,点击时暂时禁用 IntersectionObserver,800ms 后恢复。
要修复吗?
老大靠在椅背上,目光投向窗外。
大概过了两分钟,终端的声音停了。
老大,全部修复完成。
再试试看?
老大刷新浏览器——完美。点击目录里的任意标题,页面平滑滚动到对应位置,标题没有被导航栏挡住,目录的选中状态也正确对应。
"搞定了。"老大端起咖啡杯喝了一口,已经凉了,但他不在意。

尾声:导航闭环完成
周日的夕阳已经西斜,书房里的光线变得柔和起来。老大靠在椅背上,看着屏幕上的成果——
/blog文章列表页,完美/about关于页面,完美/projects项目展示页,完美- TOC 文章侧边导航,完美
404 问题解决了,网站的导航闭环完成了,长文阅读体验也提升了。
老大站起身,走到窗边,拉开窗帘往外看——夕阳把天空染成了橙红色,像老兵橙的对话气泡。窗外的玉兰花在晚风里轻轻摇晃,花瓣落了几片在窗台上。
"效率还可以。"老大心里想着,"今天搞定了 404,搞定了 TOC,健了身,看了书。这才是 AI 应该有的样子。"
他走回书桌前,在终端里输入:
收到,老大。
今日成果:
🔴 第一梯队(紧急 - 修复 404):
创建 /blog 文章列表页
修复文章详情页"返回文章列表"按钮
🟡 第二梯队(重要 - 完善网站):
创建 /about 关于页面
创建 /projects 项目展示页
🟢 第三梯队(体验优化):
TOC 文章侧边导航功能(桌面端 + 移动端)
sticky 定位 bug 修复
样式美化(宽度、内边距、间距)
锚点偏移(减去导航栏高度 100px)
选中状态同步(IntersectionObserver + 手动点击冲突修复)
网站导航闭环完成!
老大看着终端里的总结,嘴角微微上扬。"完美。"
他站起身,走到厨房,给自己接了一杯热水。饮水机发出咕嘟咕嘟的声响,水流进杯子里。他喝了一口,水温刚好。
"接下来可以搞对话气泡样式优化了。"老大心里想着,"把'巴基:'、'老大:'改成 emoji 格式,改善移动端体验。"
但今天已经够了。老大关掉终端,关掉电脑,站起身。
"明天再搞。"老大伸了个懒腰,关节发出清脆的响声,"今天收工。"
他走到客厅,打开电视——刚好是晚间新闻。老大靠在沙发上,看着电视,手里拿着一杯热水。
窗外的天已经黑了,星星出来了,一颗一颗,在夜空里闪烁。
💡 本集技术 Tips(老兵防坑指南)
-
Flex 布局 + Sticky 定位避坑:
self-start+h-fit是关键——如果 flex 子元素拉伸到父容器高度,sticky 定位会失效。 -
TOC 技术方案选型对比:服务端预提取首屏快但复杂度高,客户端 DOM 解析简单直接但有轻微延迟;内容型网站建议优先考虑方案 B(客户端 DOM 解析)。
-
IntersectionObserver 高亮同步避坑:手动滚动时需要暂时禁用 IntersectionObserver,否则自动高亮会覆盖手动设置的选中状态;方案:加
isScrollingRef标记,点击时禁用,800ms 后恢复。 -
锚点偏移最佳实践:不要依赖 CSS 的
scroll-margin-top(不同浏览器行为不一致),手动计算滚动位置更可靠——elementTop - navHeight。 -
响应式布局分层技巧:使用
xl:前缀让某些样式只在桌面端生效(如xl:min-w-[700px]),避免移动端布局错乱。
📎 关联阅读
- 《与AI合伙人相爱相杀vlog 第六集:Vlog 是怎么炼成的》
- (后续第八集待发布,敬请期待)