模块五 · AI 协作与表达
AI 协作与代码评审:Vue 项目里怎么用、怎么审
Vue 的简洁语法让 AI 更容易写对"表面",也更容易写错"响应性"。这一节给出上下文四件套、Vue 专属的五个审查点、验证三道闸,以及一份能算清的账。
读完这一节,你能做到
- 说清 AI 在 Vue 项目里最擅长什么、最容易错在哪。
- 用「目标 / 现状 / 约束 / 验收」四件套写出能用的上下文。
- 按「类型 → composable → 组件 → 测试」的顺序拆任务。
- 用五个 Vue 专属检查点审出响应性丢失、v-model 绑 prop 这类问题。
- 用
vue-tsc+ Vitest + 浏览器指标证明改动没有变坏。
一、AI 写 Vue 的强项与盲区
Vue 的 API 面比 React 窄,模板语法又规整,所以 AI 写出来的东西看起来特别像对的。问题恰恰在这里:它很容易通过"能跑"这一关,却在响应性上悄悄错。
| 它很擅长 | 它经常错 |
|---|---|
| 模板结构、指令、修饰符 | 响应性丢失(解构 reactive、把值存进普通变量) |
<script setup> 的样板 | v-model 绑到 prop 上,破坏单向数据流 |
| 简单 composable 骨架 | 缺 onUnmounted 清理、缺请求竞态处理 |
| 表单与列表的常规写法 | :key 用 index、v-if 与 v-for 同层混用 |
| CSS 与过渡类名 | KeepAlive 不写 include、Teleport 在 SSR 下取不到目标 |
判断原则:语法对不等于语义对。 Vue 的响应式是隐式的,错了往往不报错,只在某个解构、某次赋值之后界面不动。这类 bug 靠"跑一遍看看"发现不了,只能靠审。
二、上下文是新的需求文档
模型不知道你的 Vue 是 3.4 还是 3.5、用不用 <script setup>、状态放 Pinia 还是 composable、有没有装 Element Plus、团队是否允许 defineModel。你不说,它就按最常见的写法猜。
四件套,Vue 版的具体内容:
- 目标:改哪个页面、哪个交互,验收看什么。
- 现状:Vue 与构建工具版本、相关文件路径、现有 composable 在哪、状态放在哪一层。
- 约束:必须
<script setup>+ TS、不新增依赖、不许改子组件 props 签名、请求统一走useRequest。 - 验收:具体到行为——输入停顿 300ms 才请求、切换 tab 不重复请求、空数据有空态、失败有重试。
反例与正例的差距,不在字数,在约束这一项。AI 默认会给你最"教科书"的写法,而教科书不等于你的项目:
反例:帮我写一个订单列表页。
正例:目标:订单列表,支持关键词搜索与分页。现状:Vue 3.4 + TS + Vite,
Pinia 管理筛选,请求封装在 src/composables/useRequest.ts。约束:不新增依赖,
搜索词进 URL query,不许改 OrderTable 的 props。验收:输入停顿 300ms 才发请求,
翻页保留旧数据,失败显示重试。
三、拆任务:顺序比粒度更重要
Vue 项目有一个天然的优势:类型先行的成本很低。所以推荐固定顺序:
① 定 props / emit / composable 的签名
② 写 composable(纯逻辑,可单测)
③ 写组件(只负责渲染与事件绑定)
④ 补测试与边界
这样拆有三个好处:类型先定住,AI 后面写实现时幻觉字段会立刻报错;composable 是纯函数,能脱离组件测;组件层只剩渲染,审起来快。
粒度标准:这次 diff 我能在 10 分钟内逐行看完。 看不住就再拆。一次让它写"列表 + 搜索 + 分页 + 空态 + 单测",产出三四百行,你审不完,等于直接合并未知风险。
四、五个 Vue 专属检查点
按顺序过,前面不过后面不用看。
第一点:响应性有没有丢
这是 Vue 最高频、也最隐蔽的一类错误:
const { count } = reactive(state)—— 解构出来的是普通值,后续不再更新,要用toRefs。const total = list.length写在 setup 里当"派生值",列表变了它不变,应该用computed。- 把响应式对象整个存进普通变量再改,改的是副本。
ref忘了.value,或反过来在模板里多写了.value。
第二点:有没有破坏单向数据流
v-model直接绑在 prop 上 —— prop 只读,必须emit。- 子组件直接修改父组件传下来的对象属性 —— 能跑,但调用方无从追踪。
defineModel用了但没声明,或和props双写,形成两份真相。
第三点:生命周期与清理
onMounted里开的定时器、监听、订阅,onUnmounted里没清。- 请求没有取消,也没有"忽略过期响应"的标记,连续切换会显示错数据。
- 在
onUpdated里改状态做初始化,容易形成更新循环。
第四点:模板里的隐式坑
:key用 index,插入或排序后节点复用错位。v-if与v-for写在同一元素上,优先级容易判断错,应该拆一层。KeepAlive不写include,缓存了所有页面,内存白涨。
第五点:类型是否真的生效
defineProps用运行时对象声明,没有类型,vue-tsc管不到。- 出现
any、as、@ts-ignore,等于把类型闸门关掉。 emit没写类型签名,调用方传错参数也不报错。
五、验证闭环:三道闸
- 静态闸:
vue-tsc --noEmit+ ESLint +vite build。能拦幻觉 API、类型不匹配、模板里写错的属性。 - 行为闸:Vitest +
@vue/test-utils,按可访问名称查询,不测内部字段;再手测一次真实交互。 - 指标闸:首屏体积(构建产物对比)、交互延迟(Performance 面板)、渲染次数(Vue DevTools 的组件渲染统计)。
第三闸最容易被跳过,也最容易被面试追问。"没变慢"要有两个数,不能只有感觉。
六、算例:一个列表页的两种做法
任务:订单列表 + 搜索 + 分页 + 空态,以下是示意时长,用来演示记账方式。
做法 A:一次要完
| 项 | 耗时 |
|---|---|
| 写提示词、等生成 | 3 分钟 |
| review 320 行 | 30 分钟 |
| 发现筛选条件放在 Pinia、刷新即丢,重做 | 25 分钟 |
| 空态与失败态缺失,补 | 15 分钟 |
| 合计 | 73 分钟 |
做法 B:拆四轮
| 轮次 | 交给 AI 的 | 自己做的 | 耗时 |
|---|---|---|---|
| ① 签名 | props / emit / composable 类型 | 定接口与数据形状 | 8 分钟 |
| ② composable | 请求与分页逻辑 | 补竞态与错误分支 | 12 分钟 |
| ③ 组件 | 模板与事件绑定 | 空态、失败态、URL 同步 | 12 分钟 |
| ④ 测试 | 用例骨架 | 补断言 | 10 分钟 |
| 合计 | 42 分钟 |
净收益 = 生成节省的时间 − (审查 + 返工 + 事故成本)
A 的"生成"更快,但返工把它吃光了。B 把 AI 放在"产出候选"的位置,判断留给自己,总额更小、风险更低。
七、常见坑
- 被"能跑"骗过去。 Vue 的响应性错误不报错,只表现为界面不动。
- 上下文只写目标不写约束。 结果它给你 Pinia + Element Plus + axios 的全家桶,你的项目一个都没装。
- 让它一口气写完整页。 diff 一大,你只能抽查,抽查等于没审。
- 测试测实现。 mock 掉 composable、断言 class 名,改实现就红,等于没保护。
- 把业务数据贴进公网模型。 客户手机号、订单号、内部接口地址都不该出现在提示词里。
- 团队各写各的。 没有约定时,AI 会按每个人的提示词生成各自的风格,三个月后没人认得出这是同一个项目。
八、面试怎么讲
面试官问"你平时怎么用 AI",他想确认三件事:你有没有判断、有没有验证、有没有踩过坑。
怎么用:按 类型 → composable → 组件 → 测试 的顺序拆,每轮只让它产出一屏能看完的 diff
怎么验:vue-tsc + Vitest + 浏览器指标,三闸都过才合
踩过什么坑:它把筛选条件放进 Pinia,刷新就丢;后来约定筛选一律进 URL
换来什么:同类页面从 73 分钟降到 42 分钟,返工从"两次一次"降到"五次一次"
三个不要:不要说"提效 50%"却没有口径;不要说"全交给它";不要为了显得前卫而贬低不用 AI 的团队。
九、动手任务:自写 → AI 写 → 对比 → 取舍
四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。
① 自己写
- 写一个
usePaginationcomposable:接受 fetcher,返回 list / page / loading / error / empty,并处理竞态。 - 用它做一个列表页,筛选条件进 URL query,刷新后能恢复。
② 让 AI 写同一题
把第 1 题交给 AI,补一句 Vue 3.4 + TS + <script setup>,不新增依赖,必须能处理快速翻页的竞态。
③ 对比要盯的点
| 检查面 | 我的版本 | AI 版本 | 差异说明 |
|---|---|---|---|
| 响应性(解构/computed) | |||
| 清理与竞态 | |||
| 类型是否真生效 | |||
| 空态与失败态 |
④ 说取舍
- 它有没有解构
reactive或把派生值写成普通变量? - 连续快速翻三页时,它显示的是第几页的数据?
- 它的 composable 能不能脱离组件单测?不能的话你会怎么拆?
记录模板(一题一行,攒够十行就是面试案例库):
题目 | 我错在哪 | AI 错在哪 | 最终保留谁的写法 | 用时(我/AI/合计)
十、自测
- AI 写 Vue 时最容易"看起来对"的地方是什么?为什么危险?
- 上下文四件套缺哪一件最容易返工?
- 为什么推荐「类型 → composable → 组件 → 测试」的顺序?
- 五个 Vue 专属检查点分别是什么?
- 三道闸里哪一道最容易被跳过?跳过它会付出什么代价?
- 净收益怎么算?为什么"生成越快越赚"是错的?
参考答案要点:语法正确但响应性错,且不报错;缺"约束";类型与逻辑先定住、可单测、diff 小;响应性、单向数据流、清理、模板隐坑、类型生效;指标闸,面试追问时拿不出数字;生成节省减去审查、返工与事故成本。
配套题目
★ 越多越该先背;点题目可看完整答案与深入版。
- ★★★★★ AI 生成的 Vue 代码,你会重点审什么?
- ★★★★☆ 怎么给 AI 写上下文,才能一次要出能用的 Vue 代码?
- ★★★★☆ AI 写的 composable 怎么验证它真的对?
- ★★★★☆ 哪些 Vue 的活你会交给 AI,哪些坚持自己写?
- ★★★☆☆ AI 时代还需要背 Vue 的响应式原理吗?
- ★★★★☆ AI 生成的 Vue 测试为什么经常假绿?
提示:读完可以顺手做几道对应题目,检验是否真的理解