当前关键词已经变成 React,结果却显示 HTML。把页面交给 AI,补一句“搜索有问题,修一下”,它可能改请求,也可能改筛选条件。改动能跑起来,我们还得知道它修复了哪一个原因。
这一讲用慢读的故障实验页制造一个可重复的问题,再练习怎样把证据交给 AI。开始前只需要浏览器开发者工具,代码语法讲到能判断修复是否有效为止。请求和浏览器行为已按 Chrome、MDN 与 React 官方文档核对。
带着第一课的事件路径和第四课的状态归属来排查。先在实验室建立复现记录,再检查第三课原型中的加载、空结果与错误恢复。当前原型采用本地筛选,除非你增加异步搜索,不应声称它已经出现了实验室的请求竞态。
静态 HTML和静态 CSS 页面继续作为语义、布局验收基线。实验源码包含在下载包里,本文的诊断模板也整理在提示词文件中。
先把“有时候不对”变成别人能复现的步骤#
打开实验页,选择“故障模式”,点击“一键复现竞态”。页面先发出编号 1 的 HTML 查询,设定延迟 900ms;80ms 后发出编号 2 的 React 查询,设定延迟 200ms。
第二个查询先完成,结果区显示 React 资料;第一个查询稍后完成,又把它换成 HTML 资料。当前关键词依然是 React,结果却来自编号 1。时间线会记录实际经过的时间、请求编号和处理动作。设定的延迟用于制造先后顺序,实际回调时间受浏览器调度影响。
这是本地 setTimeout 模拟,没有真正向后端发出这两个搜索请求。Network 面板里找不到对应的 API 很正常。实验保留了需要研究的先后关系,去掉了登录和网络环境等干扰。
把这次观察写成一个缺陷记录。
目标页面是慢读故障实验页
复现步骤
1. 选择故障模式
2. 点击一键复现竞态
3. 等待时间线显示两个查询完成
预期
关键词是 React 时,最终资料也来自 React 查询
实际
React 结果出现后,被较早发出的 HTML 查询覆盖
已取得的证据
请求编号 2 先完成并采用
请求编号 1 后完成,也被采用
最终结果来源与当前关键词不一致
补充当前浏览器版本、窗口宽度,以及完整时间线text最小复现要保留导致故障的条件。删掉慢请求后再宣布“现在正常”,原来的问题就无从验证了。应用很大时,可以让 AI 先把一个输入框、两次异步返回和一处状态写入提取出来,确认小页面仍能复现,再改实现。
从证据决定查哪一层#
真实项目接上 API 后,一次搜索会经过更多环节。下面这张表帮助我们选择下一项检查,每次围绕一个能被证伪的猜测继续。
| 已观察到的现象 | 先查哪里 | 要留下的证据 |
|---|---|---|
| 点击后没有预期请求 | Console、事件处理与表单 | 第一条异常、调用栈、按钮是否触发提交 |
| 请求失败或返回内容异常 | Network | URL、方法、状态码、请求参数、响应与 Timing |
| 响应正确,页面显示了另一批资料 | 状态写入与请求时序 | 当前查询、请求编号、采用响应的时刻 |
| 数据正确,元素没出现或被遮住 | Elements、Computed、Layout | DOM 是否存在、最终 display、尺寸与溢出关系 |
| 输入或点击明显卡顿 | Performance | 从操作到下一帧的记录、主线程工作与长任务 |
用 Network 核对真实请求时,先选对那一次请求,再查看 Headers、Payload 和 Response。需要跨刷新保留记录,可以启用 Preserve log;排缓存问题时记录 Disable cache 的开关状态。保存 HAR 或复制请求前,移除 Cookie、令牌和私人数据。Chrome 的 Network 指南 ↗说明了这些检查入口。
状态正确而样式异常时,选中真正出问题的元素。Styles 中被划掉的声明说明它没有获胜,Computed 给出最终值,Grid 或 Flex 的覆盖层能显示布局关系。慢读卡片被长网址撑宽,就检查容器的最小宽度与文字换行规则。给整个页面加 overflow-x: hidden 会让部分内容消失,应继续查到具体元素。
截图能说明页面长什么样。请求记录和状态时间线才能帮助我们解释它为什么会变成这样。
修复竞态,要约束谁还能更新页面#
切换到“修复模式”,再执行一次相同步骤。第二个查询先完成并采用,第一个查询完成时被丢弃。修复依据是请求编号,只有当前最新请求可以提交结果。
实验中的判断可以概括为下面这个条件。
// requestId 是这次请求创建时保存的编号
// latestRequestId 会在新查询发出时递增
if (requestId !== latestRequestId) {
return
}js这个检查要放在更新可见状态之前。生产代码还要检查错误和 loading 的更新。旧请求失败时不该覆盖新请求的成功页面,旧请求结束时也不能提前关掉新请求的加载提示。
真实 fetch 可以配合 AbortController 取消已经无用的请求。组件卸载或查询变化后,也要阻止过期结果提交。取消请求能减少无用工作,提交前的有效性检查负责保证状态归属。React 官方的 Effect 示例用清理函数忽略过期响应,说明了相同的问题。React 的请求竞态示例 ↗
把输入做防抖,只会减少发出的请求数。已经重叠的两次请求仍然可能倒序完成。验收时必须故意让旧请求晚到,才能看出修复是否覆盖了这个条件。
HTTP 成功以后,数据还要过一道检查#
fetch 收到 404、401 或 500 响应时,不会仅仅因为这些状态码就进入 catch。先检查 response.ok 或具体状态,再解析响应;JSON 能解析也不代表结构正确。MDN 的 Fetch 使用说明 ↗
慢读现有数据的顶层字段是 items 和 total,每条资料包含 id、title、url、description、tags、status。把接口返回值断言成 TypeScript 类型,不会替运行中的程序校验这些字段。
让 AI 在 API 边界按约定的 schema 解析响应。至少检查 items 是数组、必填文本字段为字符串,标签是字符串数组,状态属于 unread、reading、archived,total 是非负整数。资料链接还应限制为业务允许的协议。检查失败要产生可诊断的错误,页面显示“数据暂时无法读取”,不能把异常伪装成空列表。
这里有一个后端同学容易认出的约束。未来接口支持分页后,total 可以大于当前 items.length。校验应遵循接口契约,不要根据眼前只有三条资料就写死相等关系。
空列表也需要独立处理。合法响应里 items 为空,页面应告诉用户没有匹配资料,并允许清除关键词;接口断网、格式损坏和无结果是不同状态。
鉴权、跨域和重复提交,要修在负责它的地方#
慢读将来接入账户和收藏接口时,错误响应不能统一变成“请重试”。401 表示请求缺少有效认证凭据 ↗,通常需要重新认证;403 表示服务器拒绝执行请求 ↗,重新登录未必能解决权限问题。前端显示什么按钮,由产品流程和后端契约共同决定。
CORS 错误则要核对请求的来源、预检结果、服务端允许的来源与凭据设置。不能让 AI 关闭浏览器安全策略,也不能把跨域 API 请求改成 no-cors 来“消除红字”。后者会得到 JavaScript 读不到内容的不透明响应。修复通常在服务端 CORS 配置或明确设计的同源代理上,开发代理也需要对应的生产部署方案。MDN 的 CORS 说明 ↗
收藏按钮连续点两次,是另一类职责分工。前端应在提交期间给出反馈、阻止重复操作,失败后恢复可重试状态。按钮禁用不能处理网络重试、多个标签页或请求已经到达服务器的情况。
如果产品要求一次收藏操作最多生效一次,后端需要落实幂等规则,例如用业务唯一约束,或按用户与幂等键保存一次操作的结果。同一次逻辑操作的重试沿用同一个键,新操作再换键;相同键配了不同载荷应按契约拒绝。前端与后端都要留下可关联的请求标识。POST 方法本身不保证幂等 ↗。
把下面这段诊断请求交给 AI#
先填证据,再发提示词。没有的材料写“尚未取得”,让 AI 提供采集方法。
你正在修复慢读资料库的一个前端缺陷。
请按证据定位原因,完成最小范围修复,并证明修复有效。
项目条件
- 技术栈、启动方式和涉及文件填写在这里
- 页面或提交版本填写在这里
- 浏览器、视口、网络与缓存条件填写在这里
问题记录
- 可重复的操作步骤填写在这里
- 预期结果与实际结果分别填写
- 附相关代码、Console 第一条异常与调用栈
- 附脱敏后的请求参数、状态码和响应样本
- 若涉及异步,附请求编号及状态更新时间线
- 写明哪些材料尚未取得,不要补造日志
工作要求
1. 先复现,区分观察事实与假设。给每个主要假设安排一个验证动作。
2. 根据证据定位到请求、数据解析、状态或DOM样式中的具体位置。
3. 证据足够后直接做最小修复,保留既有界面与接口契约。
4. 若涉及竞态,检查成功、失败和加载状态的过期提交。
5. 不得用隐藏内容、吞异常、类型忽略或关闭安全检查掩盖故障。
6. 不为单个缺陷改框架、引入全局状态库或新增无关依赖。
7. 跑原始复现,并验证相邻的失败场景。没有执行的验证明确标注。
请交付
- 根因与对应证据
- 修改文件、具体行为变化和必要的取舍
- 已执行的验证、实际结果、仍未验证的项目
- 回归风险及撤销本次修改的方法text这段提示词要求 AI 给出因果证据。“构建通过”只能证明一部分工程约束成立,搜索顺序是否正确仍要执行两次异步返回的实验。
用矩阵验收,别只点一次成功路径#
下表分开了当前示例可以直接执行的检查,以及接上真实服务后必须补齐的检查。不要把未来场景写成当前实验已经通过。
| 场景 | 操作或输入 | 验收结果 | 当前检查位置 |
|---|---|---|---|
| 旧查询晚到 | 一键复现竞态 | 最终采用 React,HTML 标为丢弃 | 实验页修复模式 |
| 键盘操作 | Tab 移动、Enter 提交、切换单选项 | 顺序合理,焦点可见,操作有结果 | 静态基线与实验页 |
| 窄屏和长内容 | 320px 视口,替换长标题、网址、多个标签 | 内容可读,控制项不遮挡,无整页横向溢出 | 静态 CSS 基线 |
| 表单为空 | 不填关键词便提交 | 关联字段的错误提示可见,焦点可定位 | 静态表单基线 |
| 加载、空结果、断网 | 延迟响应、合法空数组、拒绝请求 | 三种状态可区分,用户能恢复操作 | 接 API 后补测 |
| 旧请求失败 | 新请求成功后旧请求报错 | 成功结果保留,不出现过期错误 | 接 API 后补测 |
| 错误数据与鉴权 | 字段类型错误、401、403 | 按契约处理,无静默空列表 | 接 API 后补测 |
| 重复提交 | 双击、同一操作重试 | UI 反馈明确,服务器无重复副作用 | 接收藏 API 后补测 |
桌面设备模式能快速检查窄屏,最后仍要用真实手机看字体、触控和软键盘。把矩阵中的每项标为通过、失败或未执行,附上必要证据。下一次让 AI 改界面时,直接把这份记录交给它,要求重跑受影响的场景。
将故障模式与修复模式的日志分别填入工作簿的第五课,并记录一个你在自己原型中实际检查的场景。功能结果稳定后,下一课再比较同一操作的性能。