Files
huangguo_h5/.cursor/skills/frontend-project-standards/SKILL.md
T
2026-09-15 15:25:05 +07:00

259 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: frontend-project-standards
description: 为当前仓库输出可维护的 Vue3 + TypeScript 前端代码规范,覆盖组件分层、hooks 抽离、service 接口封装、错误处理与命名规范。用于新功能开发、页面重构、接口改造、复杂逻辑拆分与可维护性治理场景。
---
# 前端项目规范
## 适用范围
本规范适用于以下所有变更:
- `src/views/**`:路由级页面容器及其私有 `components/``hooks/`
- `src/components/**`:跨模块复用组件,以及形如 `src/components/<module>/{components,hooks,types.ts,index.vue}` 的"组件即模块"容器(参考 `src/components/HomeHeader/`)。
- `src/hooks/**`:全局/通用 composable。
- `src/service/**`:接口封装层。
- `src/stores/**`Pinia store。
- `src/utils/**`:纯工具函数(无状态、非 Vue 特定)。
需遵循的项目上下文:
- 技术栈:Vue 3 + TypeScript + Vite + Pinia + Vant。
- 请求链路:`src/service/*.ts` -> `src/utils/request.ts` -> axios。`request` 实例除标准 `get/post/put/delete` 外还提供 `deletes`form 形式 DELETE),不要新增同义包装。
- 路径别名:从 `src` 导入时统一使用 `@/`
## 触发条件
满足任一条件,必须先加载并按本规范输出:
- 用户消息中出现:前端规范、规范、可维护性、重构、refactor、抽 hook、抽 composable、接口封装、service、错误处理、loading/error/empty、拆分大文件、整改、治理、frontend-project-standards、@fps、/fps。
- 改动范围涉及 `*.vue``src/hooks/**``src/service/**``src/components/**``src/views/**``src/stores/**`,且包含结构、接口、状态、交互或业务逻辑调整。
- 用户提供 PRD/Figma/截图要求"按本仓库结构落地"。
- PR 自检、code review、规范审查类任务。
轻量豁免:仅改文案、样式数值、静态资源路径、纯 class 调整时,可只做最小变更并说明“不涉及分层/API/hook 调整”,无需强行套完整交付结构。
## 快捷指令
团队可直接在对话中使用以下短指令触发对应工作流,每条都映射到 `prompt-templates.md` 的具体模板:
| 指令 | 含义 | 对应模板 |
| --- | --- | --- |
| `@fps:new <页面/模块>` | 新页面或新模块开发(container + hooks + service 全套分层) | 模板 1 |
| `@fps:refactor <文件路径>` | 拆分大文件 / 抽 hook / 行为不变重构 | 模板 2 |
| `@fps:api <接口描述> -> <页面>` | 新增接口并接入页面(service typed 方法 + hook + view 三步) | 模板 3 |
| `@fps:fix <问题描述>` | 修 bug 同时做最小必要规范化 | 模板 4 |
| `@fps:review <文件/diff>` | 仅做规范审查,不大改代码 | 模板 5 |
| `@fps:hook <逻辑描述>` | 仅产出单一职责 hookstate/computed/actions | 模板 1 子集 |
调用约定:
- 任何指令默认带上"输出分层说明 + hooks 变更 + API 变更 + 错误覆盖 + 自检结果"五项交付。
- 指令后可追加自由描述,越具体越好(目标文件、行为约束、是否允许重命名等)。
- 不带任何指令时,按 `## 触发条件` 自动判定是否启用本规范。
- 若某项交付不涉及,简短标注“不涉及”,不要为了满足格式新增无意义文件或抽象。
## 强制规则(必须遵守)
### 0) 通用工程准则(最高优先级)
- 可维护性 > 短期省事;规则冲突时按 类型安全 > 分层正确性 > 局部简洁性 排序。
- **不引入新依赖**:除非用户明确要求,否则不增加 npm/yarn 依赖、不替换现有库(axios/Pinia/Vant 不替换)。
- **对齐项目结构**:所有新文件必须落入"适用范围"描述的目录形态,不另起新目录约定。
- **优先复用既有逻辑**:动手前先在 `src/hooks``src/utils``src/service``src/stores` 检索可复用项;找到 ≥70% 匹配即扩展或参数化,不重复造轮。
- **避免过度设计**:见 `### 0.1)`
- 历史代码受限暂时无法满足规则时,必须明确说明约束并给出分阶段改造计划。
### 0.1) 反过度设计(硬规则)
**默认顺序:先写最小可行实现(MVP),出现下方"复用/复杂度信号"再抽象。**
判定信号(命中任一即"过度",必须回退到更简方案):
- 仅以“未来可能复用”为理由,为只有 1 个调用方的逻辑创建 hook / 工具 / 组件;至少出现 2 处调用、明确跨页面复用价值,或命中本规范的复杂度/职责边界阈值时再抽。
- 为只有一种实参的"泛型/策略/工厂"提前预留扩展点(如未被使用的泛型参数、单实现的 strategy map)。
- 引入空壳 wrapper:组件 / 函数 / 类除转发外不做事(可直接使用被包装对象)。
- 配置爆炸:用 5+ 个布尔/枚举 props 控制一个组件多种形态,应拆为多个组件。
- 未使用的导出:新增 export 在本次改动范围内无人调用。
- 为不存在的需求设计接口字段、状态分支、loading 子态。
- 自造已被现有依赖覆盖的能力(已用 Vant/Pinia/axios 拦截器/`@/utils/request` 时不再造轮)。
- 抽象层数 ≥ 3view -> hook -> hook -> service)且中间层无独立职责。
- 单文件代码量小于 80 行却被拆成 3 个文件(除非命名/职责必要)。
- 单一页面被拆成大量细碎展示组件,组件之间只是传递原样 props/slot,无法形成清晰职责边界。
输出要求:
- 涉及"是否抽象/拆分/泛化"的决策点,必须显式给一句话理由(命中哪条复用信号或复杂度阈值)。
- 找不到理由时,默认走"内联实现 / 单文件 / 字面量参数"的最简路径。
- 抽象阈值另见 `### 2) hooks 抽离` 中的硬性拆分触发条件,二者取严。
- 页面私有 hook 因复杂副作用或业务域拆分而创建时,不要求跨页面复用;但必须具备独立职责,不能只是移动代码。
### 1) 组件分层
- `src/views/**/index.vue` 作为页面容器,负责路由参数、hooks 组装、子组件编排。
- 页面私有组件放在 `src/views/**/components/`
- 跨模块复用组件放在 `src/components/`
- 展示组件禁止直接调接口,避免承载重业务副作用。
- 模板相关逻辑留在组件内,复杂业务分支和副作用迁移到 hooks。
- 不为了“看起来分层”把一个页面拆成很多小组件。只有满足以下任一条件才拆组件:
- 该 UI 区块会复用,或未来已有明确复用场景;
- 区块模板/交互复杂,独立后能显著降低容器理解成本;
- 区块拥有清晰业务语义边界(如用户面板、订单卡片、筛选栏),而不是纯布局切片。
- 仅用于排版、单次使用且逻辑简单的片段,优先留在当前组件内。
### 2) hooks 抽离
- 满足任一条件必须抽离 hook
- 同文件存在多个副作用块(`watch``watchEffect`、生命周期);
- 一个大 `setup` 中混合了业务状态和 UI 状态;
- 逻辑具备跨页面复用价值。
- hook 命名统一为 `useXxx.ts`
- 放置规则:
- 全局/通用逻辑 -> `src/hooks/`
- 模块专属逻辑 -> `src/views/<module>/hooks/`
- hook 输出保持最小且清晰:`state``computed``actions`
- 页面私有 hook 的价值来自“隔离副作用/业务域”,不来自文件数量;单纯把几行状态搬出去不算合格抽离。
- hook 内必须清理定时器、监听器、订阅等资源。
- 大文件硬性拆分触发条件(满足任一即拆):
- 非样式代码超过 350 行;
- 单文件含 4 个及以上 `watch/watchEffect/生命周期` 块;
- 单文件混合 3 个及以上业务域(如分页 + 权限 + 埋点)。
- `src/views/**/utils/` 下的重业务文件优先迁移到 `src/views/**/hooks/`,工具函数保持纯函数化。
### 3) 接口封装
- 禁止在 `.vue` 或 hooks 中直接调用 `axios`/`fetch`
- 接口新增或调整统一在 `src/service/*.ts` 中用 class static 方法实现,并保持项目现有风格。
- 统一复用 `src/utils/request.ts``request.get/post/put/delete`
- 新增/修改的接口方法必须有入参和返回值类型(避免新增 `any`)。
- URL 路径和参数组装留在 service 层,不放到视图/组件层。
- 历史代码若仍有 `any`,本次改动不得继续扩散;应在改动范围内增量替换为明确类型。
### 4) 错误处理
- 网络层与 HTTP 状态码处理继续由 `src/utils/request.ts` 统一兜底。
- 业务异步动作仍需本地容错:
- `loading` 开关必须成对出现(`finally` 或等价方案);
- 失败时必须设置可预期兜底状态(`error`、空态、安全默认值);
- 仅在需要用户动作引导时显示用户提示。
- 禁止吞错(`catch {}`)以及仅 `console` 不落状态。
- 需要继续抛错时,必须保留明确原因与必要上下文。
- 异步后会变更 UI 状态的动作,必须覆盖失败分支并防止状态不一致。
### 5) 类型纪律
- view/hook/service 中禁止新增宽泛 `any`
- service 边界优先使用显式 `interface/type`
- 无法确认类型时优先 `unknown`,使用前做类型收窄。
- 路由参数标准化要显式处理(例如 `String(route.query.id || '')`)。
### 6) 命名规范(对齐主流最佳实践)
- 目录名统一 `kebab-case`(如 `user-profile`)。
- 非组件文件统一 `kebab-case`(如 `media-content.ts``use-xxx` 不使用,hook 文件仍保持 `useXxx.ts`)。
- Vue 组件文件名使用 `PascalCase.vue`(如 `UserProfileCard.vue`);页面入口保持 `index.vue`
- class、type、interface、enum 名称使用 `PascalCase`
- 变量、函数、方法使用 `camelCase`;常量使用 `UPPER_SNAKE_CASE`
- 布尔变量必须带语义前缀:`is/has/can/should`(如 `isLoading``hasPaid`)。
- 事件处理函数统一 `handleXxx`;计算属性使用业务语义命名,不使用无意义缩写。
- 禁止拼音、无语义缩写、单字符命名(循环计数器 `i/j` 除外)。
### 7) 历史命名兼容策略(增量治理)
- 历史文件允许保留旧命名,不强制一次性全量重命名。
- 任何新增文件、新增目录、以及被修改代码中的新增符号,必须遵循最新命名规范。
- 若本次改动涉及重命名风险较高区域,可采用“行为不变优先”的分阶段治理:本次先保证增量合规,后续单独提交命名清理任务。
- 禁止在新代码中复制旧命名反模式(即使同文件已有旧风格)。
## 交付输出要求
完成编码任务时,输出中必须包含:
1. 分层说明:哪些留在 view/container,哪些在 component/hook/service。
2. hooks 变更:新增或调整了哪些 hook、各自单一职责是什么。
3. 接口变更:新增/修改的 service 方法及其类型定义。
4. 错误覆盖:哪些异步路径已补齐失败兜底与状态恢复。
5. 维护性收益:如何降低耦合,便于后续功能扩展。
6. 自检结果:`reference.md` 中检查项的通过情况。
7. **闭环校验结果**:按 `## 完工闭环校验` 给出逐项通过/未通过,未通过需附文件/行号。
若某项未发生变更,写“不涉及”即可;不要为了输出完整而新增 wrapper、空组件、空 hook 或空 service。
## 改造工作流
按以下顺序执行:
1. 识别目标文件中的职责混杂点。
2. 必要时拆分为 container + hooks + 展示组件;组件拆分以清晰职责边界为准,不以数量为目标。
3. 将直接请求逻辑迁移到 service 层。
4. 在 service 边界与 hook 状态处补强类型。
5. 补齐 loading/error/empty 等状态保护。
6. 执行自检清单并先修复不通过项。
7. **执行 `## 完工闭环校验`**,仅针对本次改动文件。
8. 最终确认无本规范违规项。
## 完工闭环校验(仅检查本次改动)
**强制收尾步骤**。范围严格限定在"本次新增/修改/重命名"的文件,**不扩到全仓**,不修复历史问题(除非用户明确要求)。
### 必执行动作(Agent
- 对所有改动文件调用 `ReadLints`(一次性传入改动文件路径数组),确认无新增报错。
- 若改动了 `*.vue` 内的 `<script setup lang="ts">` 或新增 `.ts`,须在心中跑一遍 IDE 类型解析:所有 import 路径可达、命名/默认导出对齐、`@/` 别名能正确解析。
- 若仓库已配置 `tsc --noEmit` / `vue-tsc`,仅在用户要求时执行;默认依赖 `ReadLints` 增量结果。
### 校验清单(逐项过)
1. **类型与 lint 闭环**
- [ ] `ReadLints` 对改动文件返回 0 新增报错;历史已有报错可保留并显式列出。
2. **引入与路径闭环**
- [ ] 新增/修改的 `import` 路径存在且大小写正确。
- [ ] 命名导出 / 默认导出与源文件实际声明一致。
- [ ] `@/` 别名指向正确的真实路径。
- [ ] 仅类型用途的导入使用 `import type { ... }`
- [ ] 不留未使用的 `import`、ref、computed、props、emits。
3. **类型边界闭环**
- [ ] service 方法 params/response 类型已声明并被 hook/view 正确消费。
- [ ] 无新增宽泛 `any``unknown` 已做类型收窄。
- [ ] props / emits / defineExpose 类型完整,无隐式 any。
- [ ] 全局类型(如 `Types.*`)引用路径与 `src/typings` 实际声明一致。
4. **行为闭环**
- [ ] 异步动作 loading 开关成对,error/empty 兜底到位。
- [ ] hook 内定时器、`watch` 句柄、事件监听、订阅在 `onUnmounted` 释放。
- [ ] 模板引用的 ref/computed 都有定义且被使用,无孤儿。
5. **范围纪律**
- [ ] 改动未扩散到无关文件。
- [ ] 历史已有问题未顺手"乱改";如确需顺带修复,单独列在"附带修复"区块。
- [ ] 输出区块内显式列出"已校验文件清单"和"未校验/不在范围"的文件。
### 输出格式(强制)
完工时必须附以下区块:
```markdown
### 闭环校验结果
**已校验文件**
- src/xxx/yyy.vue
- src/xxx/hooks/useZzz.ts
- src/service/xxx.ts
**校验项**
- [x] 类型与 lint 闭环(ReadLints 0 新增报错)
- [x] 引入与路径闭环
- [x] 类型边界闭环
- [x] 行为闭环
- [x] 范围纪律
**未通过项**(如有):
- <文件:行号> <问题描述> <建议>
```
## 反模式(禁止)
- 超大 `setup` 同时处理路由、接口、业务规则、DOM 行为。
- 在模板、UI 组件或临时 util 中直接发请求,绕过 service。
- 一个 hook 混合多个无关业务域。
- 已知结构场景下继续使用宽泛 `any`
- 异步失败分支未做状态回滚或兜底。
- 对已超大文件(如 500+ 行)继续堆逻辑而不拆分。
- 把埋点逻辑混入通用分页/权限 hook。
- **过度设计**:单调用方就抽 hook/util/组件、未被使用的泛型与策略点、空壳 wrapper、5+ 布尔 props 的"全能组件"、未使用的 export、为不存在的需求设计字段或子态、自造已被现有依赖覆盖的能力(违反 `### 0.1)`)。
## PR 自检清单(必须通过)
- [ ] `.vue` 或 hook 文件中无新增直接请求调用。
- [ ] 新增/修改 service 方法均具备入参和返回类型。
- [ ] 无新增宽泛 `any`
- [ ] 异步动作具备完整 loading/error/fallback 处理。
- [ ] 长逻辑已按业务域拆入聚焦 hooks。
- [ ] 新增目录/文件/符号命名符合命名规范。
- [ ] 历史命名仅保留存量,新增/改动部分全部符合新规范。
- [ ] **未触发 `### 0.1)` 任一过度设计信号**;如有抽象/拆分动作,已说明触发的复用或复杂度信号。
- [ ] **完工闭环校验已执行且通过**(仅对本次改动文件,`ReadLints` 0 新增报错,import/类型/行为四类闭环全过)。
- [ ] 输出内容包含“交付输出要求”中的全部项目。
## 术语对照(统一用词)
- container:页面容器(`src/views/**/index.vue`)。
- presentational component:展示组件(偏 UI,无接口副作用)。
- hook/composable:可复用业务逻辑单元(`useXxx.ts`)。
- service:接口封装层(`src/service/*.ts`)。
- request layer:统一请求层(`src/utils/request.ts`)。
## 参考资料
- 代码模板见 [reference.md](reference.md)。
- 仓库场景示例见 [examples.md](examples.md)。
- 团队提问模板见 [prompt-templates.md](prompt-templates.md)。
- 命名规范详表见 [naming-conventions.md](naming-conventions.md)。