Featured Article

与 AI 合伙人相爱相杀 vlog 7:404 消失的那天

周日的回访,老大发现网站 404 问题,三个梯队排优先级,404 歼灭战,TOC 初体验,导航闭环完成。

2026年3月5日
5 min read
与 AI 合伙人相爱相杀 vlog 7:404 消失的那天

序章:发现 404 问题

"滴——"

咖啡机发出清脆的声响,一杯浓缩美式刚好落进杯子里。老大站在厨房吧台前,目光却盯着客厅餐桌上的 MacBook——浏览器开着,页面停留在博客首页。

三月的第一个周日,空气中有周末特有的松弛感。老大本来想今天休息一下:看看刚买的《AI 未来简史》,听两张黑胶唱片,晚饭做个红烧排骨。但昨天写完 Vlog 6 之后,他总觉得有什么事情没做完,心里像悬着个东西。

"先看看网站吧。"老大心里想着,端起咖啡杯走向客厅。

他在沙发上坐下,MacBook 的屏幕还亮着。首页的发光卡片很好看,"查看全部"按钮就在那儿,蓝绿色的,在深色背景下很显眼。老大想都没想就点了下去——

404 Not Found

白色的页面,黑色的文字,没有任何装饰,像一张面无表情的脸。

老大的眉头一下子皱了起来。他放下咖啡杯,在 Claude Code 终端里输入:

👨‍💻

巴基,刚点了首页的"查看全部",跳出来 404 了。

你看看导航栏,还有三个链接:/blog、/projects、/about——是不是这三个页面都不存在?

🤖

收到,老大。我检查一下路由结构……

确认了,三个页面都不存在:

/blog - 文章列表页(不存在)

/projects - 项目展示页(不存在)

/about - 关于页面(不存在)

首页"查看全部"按钮指向 /blog,所以也会 404。

另外,文章详情页的"返回内容列表"按钮指向首页,应该指向 /blog 才对。

老大靠在沙发上,目光投向窗外。楼下花园里,几个孩子正在骑自行车,笑声隐约传来。本来想休息的,看来今天又有事干了。

"这也太显眼了。"老大心里琢磨着,"用户一进来就点 404,这体验太差了。"


第一章:三个梯队排优先级

老大把 Macbook 移到书房,在台式机前坐下。机械键盘发出清脆的声响,终端已经打开,光标在那里闪烁,像一只等待指令的眼睛。

👨‍💻

好吧,今天的计划调整一下。

你整理一下,我们现在有哪些开发任务?按优先级排个序。

🤖

收到,老大。我整理一下当前可选的开发任务:

技术开发类(排除内容创作):

🔴 第一梯队(紧急 - 修复 404):

创建 /blog 文章列表页

修复文章详情页"返回内容列表"按钮

🟡 第二梯队(重要 - 完善网站):

创建 /about 关于页面

创建 /projects 项目展示页

🟢 第三梯队(体验优化):

TOC 文章侧边导航功能

Vlog 对话气泡样式优化

代码高亮与排版

建议执行顺序:第一梯队 → 第二梯队 → 第三梯队(TOC)。

理由:先解决最显眼的 404 问题,用户体验影响最大。

老大摸着下巴,三个梯队在他脑子里过了一遍——第一梯队和第二梯队都是静态页面,确实很快,加起来一个半小时就能搞定。然后剩下的时间可以集中精力搞 TOC,那个功能比较复杂。

"对了,TOC 是什么来着?"老大突然想到,"之前好像提过,但具体怎么实现还没搞明白。"

👨‍💻

可以,就按这个顺序。

不过先等等——既然 TOC 这个功能比较复杂,要不我们先研究一下 TOC 的技术方案?

比如 TOC 什么意思?怎么实现?有哪些技术方案?先把技术方案定下来,心里有底了再去搞 404。

🤖

收到,老大。这个思路也可以。

那我先跟你解释一下:TOC 是什么?

TOC = Table of Contents,也就是文章目录。

作用:

长文阅读时,右边显示一个跟随滚动的目录

点击目录项可以快速跳转到对应章节

滚动时自动高亮当前阅读的章节

显示层级:通常显示 H2(二级标题)和 H3(三级标题)

显示位置:通常放在右侧边栏,不干扰阅读

两种主流技术方案:

方案 A(服务端预提取):

优点:首屏快,SEO 友好

缺点:需要修改 Markdown 渲染逻辑,复杂度较高

方案 B(客户端 DOM 解析):

优点:简单直接,不需要动服务端逻辑,改动风险小

缺点:首屏渲染后才会显示目录(几百毫秒延迟,用户基本感知不到)

我的建议:方案 B,简单直接,适合我们这种场景。

老大端起咖啡杯喝了一口,温度刚刚好,舒服了。

👨‍💻

方案 B,就要这个。

简单直接,不用动服务端逻辑,就这么定了。

好,技术方案调研清楚了——现在可以放心去搞 404 了。

开工!第一梯队!


第二章:404 歼灭战

老大站起身,走到客厅,打开电视——刚好是体育新闻,正在播放篮球比赛。他靠在沙发上,一边看比赛,一边等巴基搞定。

👨‍💻

好,开始搞第一梯队:创建 /blog 文章列表页,修复文章详情页的返回按钮。

🤖

收到,老大。

第一梯队任务:

创建 /blog 文章列表页(与首页风格一致,展示全部文章)

修复文章详情页"返回内容列表"按钮(改为指向 /blog,文字改为"返回文章列表")

预计时间:30 分钟。

你去忙吧,有需要决策的地方叫你。

电视里,球员在球场上奔跑,篮球在篮筐上弹跳,发出"砰、砰"的声响。老大靠在沙发上,看着比赛,时不时喝一口咖啡。

大概过了十五分钟,终端的声音从书房传出来。

🤖

老大,第一梯队搞定了。

已完成:

/blog 文章列表页创建完成

文章详情页"返回文章列表"按钮已修复

要继续搞第二梯队吗?

老大从沙发上坐起来,关掉电视,走回书房。篮球比赛的比分停留在 87:82,还行。

👨‍💻

继续,搞第二梯队。

创建 /about 关于页和 /projects 项目页。

🤖

收到,老大。

第二梯队任务:

创建 /about 关于页面(博主介绍、技术栈展示、联系方式)

创建 /projects 项目展示页(展示当前项目,预留扩展)

预计时间:1 小时。

你去忙吧。

老大点点头,又走到客厅——电视已经关了,他拿起茶几上的《AI 未来简史》,继续看刚才没看完的章节。

阳光慢慢移动,客厅里的光影也在慢慢变化。老大坐在沙发上,一页一页翻着书,偶尔看一眼书房方向。

大概过了四十分钟,终端的声音又传出来。

🤖

老大,第二梯队搞定了。

已完成:

/about 关于页面创建完成

/projects 项目展示页创建完成

第一梯队 + 第二梯队全部搞定!

启动开发服务器看看效果?

老大放下书,走回书房。书已经看了六十多页,不错,今天的阅读任务也完成了。

👨‍💻

启动。

开发服务器启动,浏览器打开 http://localhost:3000。老大依次点开:

  • /blog —— 文章列表页,完美
  • /about —— 关于页面,完美
  • /projects —— 项目展示页,完美
  • 文章详情页的"返回文章列表"按钮——完美

"404 问题终于解决了。"老大点点头,"现在搞第三梯队,TOC。"


第三章:TOC 初体验(上)

老大站起身,走到书房角落的健身自行车旁。那是一辆折叠式的动感单车,平时不用的时候可以收起来,不占地方。

今天的健身任务还没完成,正好可以一边健身一边让巴基写代码。

他把手机架在车把的手机架上,又把《AI 未来简史》翻开,调整角度放在旁边。然后骑上车,调整好阻力,开始慢慢蹬起来。车轮转动,发出轻微的嗡嗡声。

👨‍💻

好,404 搞定了,现在搞 TOC。

我骑会儿健身车,顺便看会儿小说,有需要决策的地方叫我。

🤖

收到,老大。这个主意好。

我先创建 TOC 组件目录和类型定义,有需要决策的地方叫你。

老大一边蹬车,一边看书。阳光从百叶窗照进来,在地板上投下条纹状的光影。书房里很安静,只有健身车的嗡嗡声、台式机风扇的转动声,还有窗外偶尔传来的鸟鸣。

大概过了二十分钟,手机震了一下。

🤖

老大,打扰一下,有个决策需要你做。

TOC 组件的样式,你想要什么样的?

极简风格,纯文字,简单的高亮

玻璃拟态,半透明背景,发光边框,跟网站整体风格一致

其他风格

老大看着手机,手指在屏幕上轻点。

👨‍💻

玻璃拟态,跟网站整体风格一致。

继续。

老大把目光移回书本,继续蹬车。聊天在后台运行,有新消息时手机会震动。

又过了大概十五分钟,手机震了一下。

🤖

老大,又有个决策。

目录显示层级,只显示 H2,还是 H2 + H3?

只显示 H2,简洁,目录不会太长

H2 + H3,详细,方便精准跳转

老大看着手机,手指在屏幕上轻点。

👨‍💻

H2 + H3,详细一点。

H3 缩进一下,区分层级。

继续,我接着健身。

老大把目光移回书本,继续蹬车。阳光慢慢移动,地板上的光影也在慢慢变化。他已经看了快四十页,健身车的里程表显示已经骑了五公里。

又过了大概二十分钟,手机又震了一下,这次是连续好几条消息。

🤖

老大,基础组件开发完成。

已完成:

TOC 组件目录和类型定义

桌面端 TOC 组件,右侧边栏,玻璃拟态样式

移动端 TOC 组件,右下角悬浮按钮加抽屉模式

主 TOC 组件,整合桌面端和移动端

下一步,修改文章详情页集成 TOC,要继续吗?

老大慢慢停下健身车,车轮的嗡嗡声逐渐消失。他拿起毛巾擦了擦汗,额头上、脖子上都有微微的汗。里程表显示六公里,不错,今天的健身任务完成了。

他拿起手机,从车把上取下,走到书桌前。

👨‍💻

可以,继续。

修改文章详情页集成 TOC。


第四章:TOC 初体验(下)

老大在书房里走来走去,活动着刚才健身有些僵硬的肩膀。巴基在后台修改文章详情页,集成 TOC 组件,终端的代码在屏幕上快速滚动。

大概过了十分钟,终端的声音停了。

🤖

老大,集成完成了。

启动开发服务器看看效果?

老大回到书桌前坐下,端起咖啡杯喝了一口。

👨‍💻

启动。

开发服务器启动,浏览器打开 http://localhost:3000。老大点开一篇 Vlog 文章——

右侧边栏出现了,玻璃拟态样式,半透明背景,发光边框,跟网站整体风格一致。目录列出了所有 H2 和 H3 标题,H3 有缩进,区分层级。

往下滚动页面——

"不对啊。"老大皱起眉头,"目录怎么跟着一起往上走了?应该保持在同一个位置不变啊。"

👨‍💻

巴基,目录的 sticky 定位好像没生效。

我往下滚动页面,目录也跟着往上走,应该保持在同一个位置不变才对。

🤖

收到,老大。我检查一下……

找到问题了。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 后恢复。

要修复吗?

老大靠在椅背上,目光投向窗外。

👨‍💻

修复。

大概过了两分钟,终端的声音停了。

🤖

老大,全部修复完成。

再试试看?

老大刷新浏览器——完美。点击目录里的任意标题,页面平滑滚动到对应位置,标题没有被导航栏挡住,目录的选中状态也正确对应。

"搞定了。"老大端起咖啡杯喝了一口,已经凉了,但他不在意。


尾声:导航闭环完成

周日的夕阳已经西斜,书房里的光线变得柔和起来。老大靠在椅背上,看着屏幕上的成果——

  1. /blog 文章列表页,完美
  2. /about 关于页面,完美
  3. /projects 项目展示页,完美
  4. TOC 文章侧边导航,完美

404 问题解决了,网站的导航闭环完成了,长文阅读体验也提升了。

老大站起身,走到窗边,拉开窗帘往外看——夕阳把天空染成了橙红色,像老兵橙的对话气泡。窗外的玉兰花在晚风里轻轻摇晃,花瓣落了几片在窗台上。

"效率还可以。"老大心里想着,"今天搞定了 404,搞定了 TOC,健了身,看了书。这才是 AI 应该有的样子。"

他走回书桌前,在终端里输入:

👨‍💻

巴基,总结一下今天的成果。

🤖

收到,老大。

今日成果:

🔴 第一梯队(紧急 - 修复 404):

创建 /blog 文章列表页

修复文章详情页"返回文章列表"按钮

🟡 第二梯队(重要 - 完善网站):

创建 /about 关于页面

创建 /projects 项目展示页

🟢 第三梯队(体验优化):

TOC 文章侧边导航功能(桌面端 + 移动端)

sticky 定位 bug 修复

样式美化(宽度、内边距、间距)

锚点偏移(减去导航栏高度 100px)

选中状态同步(IntersectionObserver + 手动点击冲突修复)

网站导航闭环完成!

老大看着终端里的总结,嘴角微微上扬。"完美。"

他站起身,走到厨房,给自己接了一杯热水。饮水机发出咕嘟咕嘟的声响,水流进杯子里。他喝了一口,水温刚好。

"接下来可以搞对话气泡样式优化了。"老大心里想着,"把'巴基:'、'老大:'改成 emoji 格式,改善移动端体验。"

但今天已经够了。老大关掉终端,关掉电脑,站起身。

"明天再搞。"老大伸了个懒腰,关节发出清脆的响声,"今天收工。"

他走到客厅,打开电视——刚好是晚间新闻。老大靠在沙发上,看着电视,手里拿着一杯热水。

窗外的天已经黑了,星星出来了,一颗一颗,在夜空里闪烁。


💡 本集技术 Tips(老兵防坑指南)

  1. Flex 布局 + Sticky 定位避坑self-start + h-fit 是关键——如果 flex 子元素拉伸到父容器高度,sticky 定位会失效。

  2. TOC 技术方案选型对比:服务端预提取首屏快但复杂度高,客户端 DOM 解析简单直接但有轻微延迟;内容型网站建议优先考虑方案 B(客户端 DOM 解析)。

  3. IntersectionObserver 高亮同步避坑:手动滚动时需要暂时禁用 IntersectionObserver,否则自动高亮会覆盖手动设置的选中状态;方案:加 isScrollingRef 标记,点击时禁用,800ms 后恢复。

  4. 锚点偏移最佳实践:不要依赖 CSS 的 scroll-margin-top(不同浏览器行为不一致),手动计算滚动位置更可靠——elementTop - navHeight

  5. 响应式布局分层技巧:使用 xl: 前缀让某些样式只在桌面端生效(如 xl:min-w-[700px]),避免移动端布局错乱。


📎 关联阅读