摘要: Vue 项目最怕的不是需求多,而是代码慢慢变成“谁都能写、谁都不敢改”的样子。 Props 被子组件顺手改掉,watch 越堆越多,模板里塞满三元表达式,接口请求藏在组件深处。

这篇文章记录我把 F2E Spec 改成 Vue 规范 Skill 之后,怎么用它写组件、做代码审查、重构旧页面。 不讲空泛原则,直接给能复制进 Codex 的提示词。

📌 说明: 这份 f2e-spec-vue Skill 保留了原 F2E Spec 的通用规范,只替换了 React 相关内容, 现在重点约束 Vue 3、TypeScript、<script setup>、Props、事件、响应式状态和 Template 写法。

📌 前言:我被 Vue 代码“教育”了几次

以前我觉得,前端规范这种东西,有 ESLint 不就够了吗?

直到后来接了几个已经跑起来的 Vue 项目,我才发现,能通过 lint 只是最低要求。

第一次,是一个用户编辑弹窗。保存按钮点下去没问题,但改完头像后,列表页的数据莫名其妙提前变了。往下找才发现,子组件直接改了传进来的 Props。

第二次,是筛选页面。页面里只有三个筛选项,却写了七八个 watch。改一个日期范围,接口触发两次;切换路由回来,旧请求又把新数据覆盖了。

第三次,是一个“简单”的表格组件。模板里塞着权限判断、状态转换、金额格式化和一串嵌套三元表达式。需求说把“待审核”改成“审核中”,我盯着那段模板看了十分钟,愣是不敢下手。

这几次问题表面上不一样,实际上就一件事:

代码没有边界,规则也没有落地。

🎯 这份 Skill 适合谁

  • ✅ Vue 3 + TypeScript 项目正在持续迭代的人

  • ✅ 经常让 Codex、Claude Code 或 Cursor 帮忙写前端代码的人

  • ✅ 接手旧 Vue 项目,想重构但又怕改坏功能的人

  • ✅ 团队里既有 Options API,也有 <script setup>,需要统一底线的人

  • ✅ 想让 AI 少写一点“看起来完整,实际上难维护”的代码的人

🧩 它到底管什么

f2e-spec-vue 不是替代 ESLint 的工具。

ESLint 擅长抓格式和语法问题;这份 Skill 更像是在 AI 写代码之前,先提醒它别把项目写歪。

| 常见问题 | Skill 会关注什么 | | --- | --- | | 子组件改了 Props | 要求通过 emit 通知父组件更新 | | watch 满天飞 | 派生状态优先用 computed,watch 只处理副作用 | | 模板像一锅粥 | 复杂判断移到 computed 或命名函数里 | | 列表更新错位 | v-for 使用稳定唯一的 key,不拿下标凑数 | | 新组件和旧项目打架 | 新代码优先 Vue 3 + Composition API,旧 Options API 不强行重写 | | 为了优化引入一堆复杂逻辑 | 先测量,再考虑 v-memo、缓存、虚拟列表 |

🛠️ 实战一:不要再说“帮我写一个弹窗”

❌ 我以前的说法

帮我写一个用户编辑弹窗,支持修改姓名、手机号和头像。

这种指令看着没问题,实际留给 AI 的自由发挥太多了。它很可能会:

  • 把接口请求、表单状态、上传逻辑和弹窗展示全塞进一个组件;

  • 直接修改 props.user

  • 为了同步数据写几个 watch;

  • 把校验、提交和错误处理混在一个几十行函数里。

✅ 加上规范后的说法

请按 f2e-spec-vue 规范实现一个用户编辑弹窗。

技术栈:
- Vue 3
- TypeScript
- Element Plus
- 使用 script setup

要求:
1. 接收 user 作为 Props,但不能直接修改 Props。
2. 编辑状态使用本地 ref 或 reactive 管理。
3. 保存成功后 emit submit,关闭时 emit close。
4. 接口复用现有 api/user.ts,不新建请求封装。
5. 必须处理 loading、校验失败和请求失败。
6. 不新增依赖,不做无关的抽象。

这段提示词没有变得多复杂,但它把边界交代清楚了。

AI 知道哪些数据归父组件,哪些状态归弹窗,接口该从哪里来,失败时不能悄悄吞掉。

🔍 实战二:审查一段“能跑但不舒服”的代码

很多时候,最有价值的不是让 AI 从零写,而是让它帮你指出一段旧代码到底哪里别扭。

按 f2e-spec-vue 审查 src/views/order/OrderList.vue。

只检查以下内容:
- Props、emit 和 v-model 的使用是否合理
- watch 是否承担了不该承担的工作
- v-for 的 key 是否稳定
- 模板内是否有复杂表达式
- 是否存在直接操作 DOM、危险 HTML 渲染或废弃 Vue API

输出格式:
1. 文件和位置
2. 问题说明
3. 最小修改建议
4. 不要重写整页代码

最后一句“不要重写整页代码”很有用。

很多 AI 在审查时一兴奋,就把一个局部问题升级成架构改造。实际项目里,大多数时候我们需要的是先把坑填平,不是顺手装修整栋楼。

🔧 实战三:重构旧组件,不把项目搞翻新

老项目里最常见的误区,是一看到 Options API 就想全部换成 <script setup>

这不一定错,但经常没必要。

将下面的 Vue 组件重构为符合 f2e-spec-vue 的写法。

限制:
- 保留现有 Options API,不迁移到 Composition API。
- 不改变接口、事件名称和页面行为。
- 删除重复状态。
- 将模板中的复杂判断提取到 computed 或 methods。
- 保留已有组件库用法。
- 先说明准备修改哪些地方,再输出代码。

规范不是为了追新。

一个稳定的旧组件,能把状态理顺、把重复逻辑收起来、把模板看明白,已经是一次很值的重构。

📋 一份我常用的 Vue 页面提示词

请按 f2e-spec-vue 规范实现页面。

项目环境:
- Vue 3 + TypeScript + Vite
- Pinia
- Vue Router
- Element Plus

页面需求:
- [写清楚功能]
- [写清楚接口]
- [写清楚交互]

约束:
1. 页面局部状态不要放进 Pinia。
2. 跨页面共享状态才允许进入 Store。
3. API 必须复用现有请求模块。
4. v-for 使用稳定唯一的 key。
5. Props 不可直接修改。
6. loading、空数据、错误状态都要处理。
7. 不新增依赖,不创建没有实际用途的抽象。
8. 完成后列出改动文件和验证方式。

这套写法的重点不是把 AI 管得很死,而是减少那些本不该出现的猜测。

需求边界清楚,AI 才不会顺手给你造一个新 Store、一套新请求层,或者一个没人维护的工具函数集合。

⚠️ 它不能替你做什么

  • 不能替你确认接口字段是否真的存在,接口文档还是要看。

  • 不能替你判断业务规则对不对,“审核中”和“待审核”到底差什么,还是得由人说清楚。

  • 不能替你跑测试,规范能减少问题,不能证明代码一定没问题。

  • 不能替代项目已有约定,如果项目已经有 ESLint、Prettier、组件库规范,优先服从项目本身。

💡 最后一点体会

我现在更愿意把这份 Skill 当成一个“开工前的提醒”。

写组件前,先想清楚数据从哪来、谁负责改、改完通知谁;写 watch 前,先问一句这是不是本来该用 computed 解决;想加一层封装前,再问一句它到底帮谁省了事。

Vue 的灵活很好,但项目越大,越需要一点克制。

f2e-spec-vue 做不了架构师,也替不了代码审查,但它至少能在 AI 准备把简单事情写复杂的时候,拉你一下。

代码能跑,只是开始。

代码能被后来的人看懂,才算真的写完。