上个月我接了一个内容站点的详情页改造需求。页面的路径大致是 /posts/[id],但困难在于:不同内容类型下同一个 id 要展示完全不同的模块,还要带上阅读进度、收藏状态、上一篇下一篇、相关推荐,并且从列表页进入时要保持原有浏览位置。我不打算手写一堆散乱的 fetch 和 if 判断,所以直接用 Claude Code 来做。第一版提示词我只写了一句话:“帮我写一个动态路由详情页面。”结果它生成了一大坨看起来能跑、一联调就出问题的代码。
问题不出在代码生成能力上,出在提示词没有把“动态路由详情页”背后的复杂性描述清楚。后来我换了一种思路,把复杂页面当成一个“带多状态、有竞态条件、会反复进入和退出”的系统来描述,Claude Code 的输出质量和完成度立刻不一样了。这篇东西不是讲 Claude Code 怎么安装的,而是分享一套我梳理过后可以复用的提示词结构,专门解决“复杂动态路由详情页面”这一类需求。适合正在用 Claude Code 或同类 AI 编程工具做真实前端项目的人参考,尤其是那些已经发现“对话式写代码”一开始很惊艳、碰到复杂页面就失控的人。
1. 复杂动态路由详情页,为什么AI编程特别容易翻车
1.1 一句话需求只会换来AI的“自由发挥”
我见过很多朋友给 Claude Code 的原始提示词是这样的:“帮我写一个商品详情页,路由是 /products/[id],要有轮播图、SKU选择、推荐商品、规格参数,接口你自己猜一下就好了。”这句话信息量其实很低,但 Claude Code 在对话式界面里很容易给人一种“它懂了”的错觉。
它确实会给你一个看起来结构完整的详情页页面,文件、组件、样式全都生成出来了。问题会在你真正接入接口后集中爆发:接口字段是 productId,页面里用的是 id;详情页从列表点进来需要保留滚动位置,但这个组件在路由切换时被整个卸载了;用户在 /products/123 和 /products/456 之间跳转时,组件并没有重新挂载,但里面的数据请求逻辑还在用旧 id,最后页面出现两三个不同商品来回闪烁。
这些不是 Claude Code 能力不够,而是它只能根据你给的信息做“最可能的推测”。你不告诉它动态路由详情页的真正约束条件,它就按照通用模板给你拼一个。站在它的角度看,一句话需求代表你的需求就是“做个能看的页面”,不是“实现一个可用的入口页面”。这是第一层翻车原因。
1.2 详情页真正复杂的地方不在“页面长什么样”,而在路由之外的副作用
很多人以为详情页的复杂度在 UI:头部漂亮、模块丰富、交互点多。但实际上详情页开发最容易出 bug 的地方几乎都和路由参数的生命周期有关。
直接说几个真实场景。用户从文章列表进入 /posts/123,然后侧边栏又推荐了 /posts/456,点击后浏览器 URL 变了,页面组件却没有被销毁重建。此刻页面里如果有模块依赖“当前文章的 id”去请求数据,那它的 useEffect 依赖项必须正确响应参数变化。再比如用户在详情页多次点击“重新加载”,结果前一个慢响应比后一个快响应晚回来,把后一个正确结果覆盖了,这种竞态条件在接口慢的时候特别明显。
还有一类隐藏问题:详情页标题、分享链接、二维码内容都依赖当前 URL 中的路径参数,必须同步变化。如果 Claude Code 只把它理解成“一个普通的详情展示页”,那我上面说的任何一个细节它都想不到。因为它没有能力自动知道你页面上有哪些东西和 URL 参数强绑定,只能靠提示词把这类约束写清楚。
1.3 动态路由详情页的本质是一个“参数状态机”
我自己最常用的一套理解方式,是把动态路由详情页看成一个由 URL 路径参数驱动的状态机。当前路径是 /products/[id],整个页面状态就包括:当前 id 是什么、上一次 id 是什么、当前请求处于 loading 还是 error 还是 success、页面滚动位置是否应该保留、如果 id 不存在要不要显示 404、从当前页跳转到另一个同类型详情页时哪些模块需要重置。
当你在提示词里用这个角度描述问题时,Claude Code 生成的代码逻辑会发生两个明显变化。第一,它不会把数据请求直接散落在页面组件里,而是会考虑一个更合理的请求状态容器。第二,它会为“路由参数变化”这个事件单独设计一套处理逻辑,而不是让组件只匹配第一次进入。
提示词工程到这个阶段,就不是为了让 AI 少改几行代码,而是让它理解系统边界。复杂动态路由页面的系统边界恰恰是:参数变了,整个页面的状态要如何清理和重建;参数没变,哪些模块的缓存可以继续使用。我希望你也能把 Claude Code 当成一个需要做系统设计合作者,而不是只会补全代码的生成器。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开工前把需求“翻译”成Claude Code能执行的任务边界
2.1 一个反面案例:直接把零散需求扔给AI
为了说清楚差距,我放一个典型的零散需求原文,很多人在聊天里就是这样写的:“我想做个 /posts/[id] 详情页,需要正文、标题、作者、相关文章。要好看点,响应式。接口大概是 GET /api/posts/{id},返回 data。然后要从列表页点进来,还要能点赞。最好加上骨架屏。”
这条需求里的问题太多了:接口返回结构不明确、点赞操作没有说需不需要登录态、相关文章的数据来源没有说、滚动位置到底保留到哪种程度也没说。Claude Code 只会做两件事:挑其中能直接翻译成代码的部分,再脑补剩下的部分。结果就是你验收时要面对一堆“我怎么没让你加这个”或者“这里实现方式和我们项目约定不一致”。
如果你在真实项目里用它,就必须把这些零散想法先整理成任务边界,再喂给它。AI 编程并不是“会说话就能写代码”,而是“能把需求描述成约束和验收条件时才能稳定写好代码”。这一点做产品经理的朋友应该很熟:需求描述没有边界,开发必然要返工。Claude Code 就是那个不管你需求多模糊都会硬着头皮写的开发,提示词就是你把需求讲清楚的手段。
2.2 我把需求拆成四个区块的固定套路
我自己习惯把详情页类需求按四块来组织,顺序固定,写起来非常快。
第一块是上下文:包括项目技术栈、仓库里已有的目录约定、这次任务涉及的具体文件路径。如果这是一个已有的老项目,我一定会写“先查看 docs/architecture.md 和 src/app/posts 下的现有代码,遵守项目已有写法”,这句话能让 Claude Code 先读文件再动手,而不是凭自己的训练记忆生成另一套风格。
第二块是任务目标:把动态路由详情页明确到“路由路径是什么、入口文件是哪个、页面要包含哪些模块”。不要写得像产品宣传语,尽量写成可命名的模块列表,比如“文章正文区、作者卡片区、相关文章区、底部导航”。
第三块是约束:包括数据请求方式、加载状态如何处理、鉴权要求、参数变化的响应规则、SEO 要求。这里宁可多写几条,也不要让它自由发挥。
第四块是验收标准:写完以后跑哪些命令验证、打开哪些路径检查、哪些行为必须通过手动测试确认。比如“在 /posts/1 与 /posts/2 之间切换,页面不能出现上一个 id 的数据残留”。
2.3 放对话里还是放进 CLAUDE.md?
有很多人问:这套东西每次都写太长了,能不能直接写进 CLAUDE.md?我的答案是两者分工不同。
如果只是当前一个详情页的独立需求,直接放在提示词对话里最好,因为它包含非常多和本次改动相关的临时信息,写进长期记忆文件反而会造成噪音。但如果你的项目里有很多动态路由详情页,并且你希望 Claude Code 每次生成相关页面都默认遵守同一套要求,那就应该把项目级规范写进 CLAUDE.md。
比如我经常在项目的 CLAUDE.md 里放这样的规则:所有详情页默认必须包含 loading.tsx、error.tsx、not-found.tsx;动态参数 id 必须使用参数对象传入,不允许从全局状态里读;详情页内发请求必须封装到 src/lib/fetchers.ts;同类型详情页切换时必须重置数据状态。这样一来,你在新对话里只写“给新栏目增加一个动态路由详情页”,Claude Code 读仓库时也会自动遵守这些约定,提示词长度就可以大幅压缩。
3. 一段可复用的核心提示词:让Claude Code先出路由方案再动手
3.1 我拿实际项目调的提示词样例
下面这段提示词是我在 Next.js App Router + TypeScript 项目里实践过的一个版本,虽然我建议你先按自己的项目路径微调,但整体结构可以直接照抄。
角色:你是熟悉这个仓库的前端工程师。先查看 package.json 和 src/app 下的结构,不要假设技术栈版本。
任务:实现文章动态路由详情页
/posts/[id],页面入口是src/app/posts/[id]/page.tsx。功能模块:
- 主体区域展示标题、发布时间、作者名、正文;
- 作者信息卡片,包含头像、简介和“查看作者主页”入口;
- “上一篇/下一篇”文章切换;
- 相关文章列表,来源接口
/api/posts/{id}/related。数据接口:
GET /api/posts/{id},响应格式为{ code, data: { id, title, content, author, publishedAt } };- 用
code !== 0作为接口错误判断。核心约束:
- 从详情页内切到另一个同类型详情页时,相关请求必须基于新 id 重新发起,不能用上一次的数据;
- 不得产生请求竞态覆盖;滞后的旧请求响应不能覆盖新请求的数据;
- 页面加载中显示骨架屏,请求失败显示错误态,文章不存在或 id 非法时走 notFound;
- 从列表页进入时的滚动位置由路由级滚动恢复负责,不要在详情页里写全局滚动锁;
- 标题、面包屑、详情内容都要根据当前 id 变化同步更新。
操作流程:
- 先不要修改代码,先读相关文件和路由配置,输出你的实现方案;
- 方案里必须说明你打算用哪一层读取路径参数、如何处理 id 变化和竞态;
- 等我确认方案后,你再开始实现;
- 实现时只改当前任务需要的文件,不要顺手格式化其他文件。
完成前,先自己检查:
tsc --noEmit是否通过,再告诉我你修改了哪些文件。
很多第一次看这段提示词的人会觉得太长,但实际用起来反而比短提示词省时间。因为它把最容易返工的点提前变成了规则,Claude Code 不需要在生成后再猜你要不要竞态处理。
3.2 为什么我先逼它输出方案,而不是让它直接写代码
我见过很多 Claude Code 用户的习惯是提示词最后加一句“请直接生成代码,不要解释”。这个习惯在生成单文件小脚本时没问题,在复杂动态路由详情页上是个灾难。原因特别简单:如果不先出方案,你就看不到它对“路径参数、数据请求、路由状态”这几个核心问题的理解是什么。
当你让它先输出实现方案时,它至少会暴露三件事:它打算在页面组件里直接写 fetch 还是封装成一个自定义 hook;它有没有意识到 /posts/[id] 的动态参数需要从路由系统取,而不是从 URL query 里读;它打算怎么区分 loading、error、404 三种状态。你不需要完整读懂每一行代码,只要能听出这三个问题的处理方式,就能判断这个页面后面会不会反复改。
我实际的经验是:Claude Code 输出方案后,如果我发现它把 useParams 和 useSearchParams 搞混了,就直接在这里纠正它,而不是等代码写完之后再去改一坨已经成形的文件。方案阶段的纠错成本可能只是一句话,代码阶段的纠错成本可能是几个文件的重写。
3.3 方案出来后,我会追问的三个问题
Claude Code 给出的方案大多数时候看着合理,但我会按页面类型额外追问几个问题。如果是 Next.js 项目,我会问它:这个项目里 params 是同步对象还是异步 Promise?你打算在哪一层 await?这个问题能防止它把 Next 15 项目的异步参数用旧写法处理。
第二问是数据请求和渲染方式的关系:这个详情页需要首屏 SEO 吗?如果答案是需要,我会要求它优先使用服务端组件取数路径,至少把标题和正文首屏内容做成可被链接预览抓取的结构。如果这是后台管理端页面,不需要 SEO,我就会让它专注于客户端交互体验。
第三问往往会问在失败状态:详情页在请求失败时,是做一个带重试按钮的局部错误态,还是直接显示整页错误?这两个策略影响组件结构很大。有时候我还会追问“从其他详情页切换过来时,如果新 id 对应的接口还在请求中,上一篇文章的页面是否应该立即被清空”。每次追问完,Claude Code 方案里的边界就完整一圈。
4. 动态路由详情页的硬骨头:让代码在“参数变化”时依然正确
4.1 同一个组件、不同参数:动态路由最容易踩坑的分水岭
动态路由页面最独特的场景是:浏览器从 /posts/123 跳到 /posts/456,对于前端框架来说,页面组件本身没有被卸载,只是路由参数变了。如果你的页面代码只在组件首次挂载时执行一次数据请求,那从 123 跳到 456 时,页面内容就会持续停留在 123,直到你手动刷新。
这个场景在传统的多页开发里是不存在的,因为每次跳转浏览器都会重新拉一次 HTML;但在现代单页应用、App Router 客户端导航场景里非常普遍。我让 Claude Code 写详情页时,一定会把这条变成一个显式规则:所有依赖路径参数的数据副作用,都必须把参数放入依赖项,并且要在依赖变化时重建状态,而不是直接复用上一次结果。
提示词里可以写得非常具体。比如“当组件收到新的 id 时,先把当前 data 清空或置为 loading,再发起新请求”,这比空泛的“处理参数变化”要可操作得多。Claude Code 能理解 clear-then-fetch 这个模式,只要你把这个模式作为规则写进约束区域。
4.2 我在提示词里给它的“防呆规则清单”
经过多次翻车之后,我整理出了一份固定会写进提示词里的防呆规则,针对复杂动态路由详情页特别有效。
第一,请求竞态必须处理。实现方式可以是用 AbortController 取消上一个请求,也可以是用请求序号标记,保证只有最新一次请求的结果能写入状态。我不限定它用哪种,但要明确“旧的响应绝对不能覆盖新的响应”。
第二,参数变化不能让页面静默停在旧数据上。最合理的做法是在 id 变化时先把 data 置空并进入 loading,或者至少显示一个顶部加载条,让用户意识到内容正在切换。千万不要以为这个行为是默认的,AI 如果没有被显式要求,通常会忽略。
第三,页面销毁时清理副作用。如果详情页里有轮询、定时器、全局事件监听、观察者对象,那在组件卸载时必须释放。否则用户从 /posts/123 跳到 /settings 时,可能还在偷偷发起定时请求,浪费用户流量也容易报错。
第四,SEO 相关数据不能只依赖客户端渲染完成。如果 Claude Code 生成的页面是纯客户端组件并且所有内容都等 fetch 完成才出现,我会要求它考虑使用服务端组件取首屏内容,或者把关键 meta 信息放到 generateMetadata 里按 params 动态生成。这决定了分享卡片和搜索引擎能否抓到标题。
我把这些规则列成一个表给 Claude Code 的效果,比在提示词里写“优化用户体验”好得多。规则不是形容词,而是可验证的行为约束。
4.3 Claude Code 经常出错的位置和我用的兜底检查
不管提示词写得多么完整,AI 生成的代码还是会有小概率遗漏。我建议你在拿到代码后,按下面几个点快速过一遍,比通读几十个文件效率高得多。
首先是 grep 项目里详情页相关的 useEffect,看依赖数组里是否包含路由参数。如果不包含,这就是一个定时炸弹。然后是找详情页的请求函数,看它是从组件参数里拿到 id 还是从全局状态或上次调用的闭包里拿 id。后者在切换详情时容易拿到旧值。再就是看有没有一个能代表“当前是否还有未完成的请求”的标记,如果接口发出去就没人管结果,那大概率有竞态隐患。
我有一个非常实用的兜底检查方法:在 Claude Code 实现完以后,下一步提示词不是“看起来不错”,而是“请在这个页面里临时加入一个慢接口模拟器,让第一个请求延迟 3 秒返回,第二个请求延迟 0.5 秒返回。然后在浏览器里快速从 /posts/1 切到 /posts/2,观察最终停留的是哪个 id 的数据。”这个测试能非常直观地暴露竞态覆盖问题。如果 Claude Code 无法自动操作浏览器,我会自己手动测一遍,通常几分钟就能跑完。
5. 多轮对话里的纠错锚点:怎么让Claude Code不越改越乱
5.1 单轮写完一个详情页是侥幸,多轮纠错才是常态
我在真实项目里很少一次性让 Claude Code 把整个动态路由详情页写到完美。更多时候是它写完了主体,我人工看一遍,发现某个细节不对,再通过提示词让它修正。问题就出在这个环节:很多人在第二轮就开始乱掉了。
最典型的表现是第二轮的提示词写“这个页面还是有问题,你重新帮我写一下”。这种纠错方式相当于让 Claude Code 把代码推倒重来,它不仅可能丢失前面已经改对的东西,还可能因为不理解你预期行为而对细节做新的猜测。结果过了十分钟,你发现改出来的页面满意程度比第一版还低。
多轮纠错的关键不是反复说“不对”,而是建立一个可以被 AI 定位的“语义锚点”。语义锚点包括:出问题的组件或函数名、你操作它的具体路径、你观察到的错误行为、你预期的正确行为、你允许修改的文件范围。五样里至少具备四样,纠错才能精准落地。
5.2 用“问题+期望+范围”的格式代替泛泛抱怨
我自己会把纠错请求写成这种格式,几乎每次都有效:
运行到
/products/123后,点击侧边栏的/products/456,Network 面板里请求还是GET /api/products/undefined,页面最后显示了 123 的数据。我怀疑是 ProductDetail 组件里getProductDetail` 调用从上一次点击的上下文里拿了 id。请检查这个组件里 id 的来源,目标是从路由参数读取最新值,并在参数变化时重新请求。只修改 ProductDetail 以及它直接依赖的请求封装,不要动页面布局。
这段提示词的效果和“这个页面有问题”完全不一样。它告诉 Claude Code:用什么路径复现问题、判断标准是什么、问题可能出在哪个函数、修改边界是什么。Claude Code 可以直接定位到相关代码,而不是满仓库搜索“到底哪个页面”。
还有一个我常用的技巧:如果页面上某个错误只在点击后出现,我会要求 Claude Code 先加一个 console.log,把每次请求的参数打出来,先确认参数来源,再谈改逻辑。这比直接让它猜要快。你可以在提示词里说“不要直接删除日志,先运行一次,把日志给我”。在命令行工具里它能直接帮你跑起来看结果,非常方便。
5.3 上下文太长或Claude Code开始“失忆”时的处理
用 Claude Code 写复杂页面,一个现实问题是长对话后 token 上下文越来越长,它可能会遗漏你开头提示词里写的某条约束。这不是幻觉胡说,是任何对话式工具面对上下文压缩时都可能出现的问题。处理办法不是指责它“记性差”,而是主动重新建立关键约束。
我建议你在对话超过二三十轮后,或者发现它对前面的重要规则理解明显弱化时,不要继续叠话。先整理一份“不变规则摘要”,把开头最重要的几条约束再贴一次,比如“动态 id 变化必须重置数据”“不允许竞态覆盖”“不改动与当前任务无关的文件”。这能有效阻止它在后续修改里引入风格漂移。
另一个更省 token 的做法是用 /clear 开启新会话,然后把已经确认的关键位置和文件清单放进去,让 Claude Code 基于当前文件状态继续修改。你在新会话里不需要把全部讨论过程复述,只需要说“项目详情页已实现大部分功能,现在需要修复一个问题:...”,并附上问题复现路径。这一招能明显降低上下文污染带来的乱改概率。
6. 沉淀成自己的提示词模板:从一次性对话到持续可用的技能文件
6.1 在CLAUDE.md里固化项目级规则
很多人用 Claude Code 的辛酸是:同一个错误改了三遍,每次换个新对话又重新踩一遍。要打破这个循环,最简单的方式是把上一节那些防呆规则写进项目的 CLAUDE.md 文件,让每次启动对话时 Claude Code 都能读它。
我会把动态路由详情页的规则分成两类。一类是“所有详情页都适用”的通用规则,比如参数变化必须清空旧数据、请求必须处理竞态、卸载时清理副作用、错误态和 loading 态不能缺失。另一类是“当前项目特有”的规则,比如项目里固定使用 axios 实例、接口响应包裹格式是什么、详情页文件必须放在哪个目录下、生成页面时必须使用现有的页面容器组件。
写进 CLAUDE.md 以后,你每次开新对话都能省去重复粘贴一长串规则的时间成本,同时保持项目代码的统一性。如果你带过团队,可以把它理解为写给 AI 同事看的开发规范,和给人类同事的开发规范本质一样。
6.2 用斜杠命令或自定义技能保存动态详情页提示词模板
单项目规则可以放 CLAUDE.md,但跨项目通用的“动态详情页开工模板”更适合做成一个自定义命令或技能文件。Claude Code 支持自定义斜杠命令,你可以把前面第 3 节那段核心提示词存成一个模板,在每次接到详情页需求时直接调用,再补充当前项目的路径、接口和技术栈。
这个模板文件不需要写得像论文,它是一个填空题,核心占位符包括:路由路径、入口文件、接口文档、页面模块、特殊约束、需要先确认的问题。每次调用时我会让 Claude Code 先告诉我这几个占位符的缺失项,再执行实现。它本质上不是“给你写代码的魔法咒语”,而是“逼你把需求想完整的问题清单”。
我还在模板末尾固定放一句“如果发现需求有不明确的地方,先向我提问而不是自己假设”。这句话会显著减少 AI 对你的项目架构做出大胆假设的概率。如果发现它问你太多问题,反而说明这个任务本身模糊度高,值得多花一轮来澄清。
6.3 每次踩坑后,把修正追加回模板
制作模板最有价值的地方不是一开始写得多完美,而是持续演进。每当我发现 Claude Code 在动态路由详情页上因为某类问题翻车,如果这个问题具有普遍性,我就会把它追回到模板的“核心约束”或“验收清单”区域。
举个例子,有一次我发现 Claude Code 生成的详情页在从前一个详情页切到另一个详情页时,页面顶部的标题更新了,但浏览器标签页的 document.title 还停留在上一个商品的标题。这个 bug 不在代码报错范围内,纯靠人工浏览才能发现。于是我在模板里加了一条:“切换详情页时,浏览器标签页标题、面包屑、分享 meta 信息必须同步更新。”之后同类任务就不再犯这个错。
同样,如果你发现某个错误的接口字段名频繁被 Claude Code 猜错,就把项目的接口返回示例写进 CLAUDE.md 或模板中的接口部分。AI 编程工具的长期价值,一半靠基础模型本身的能力,另一半靠你把项目知识和踩坑经验系统化地喂给它。提示词工程到后面拼的就是你的反思和沉淀能力。
我自己现在做一个复杂动态路由详情页,第一轮提示词反而写得比过去长很多,但后续修改轮数可能只有过去的三分之一。每次拿到代码,我还是会快速检查参数变化、竞态和副作用清理这三件事,并不是不相信 Claude Code,而是把 AI 当作一个写代码速度很快、但对系统边界经常想当然的工程师。好的提示词不会让 AI 变得万能,但能明确告诉它哪些地方不能想当然。
