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

16 KiB
Raw Blame History

name, description
name description
frontend-project-standards 为当前仓库输出可维护的 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 外还提供 deletesform 形式 DELETE),不要新增同义包装。
  • 路径别名:从 src 导入时统一使用 @/

触发条件

满足任一条件,必须先加载并按本规范输出:

  • 用户消息中出现:前端规范、规范、可维护性、重构、refactor、抽 hook、抽 composable、接口封装、service、错误处理、loading/error/empty、拆分大文件、整改、治理、frontend-project-standards、@fps、/fps。
  • 改动范围涉及 *.vuesrc/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/hookssrc/utilssrc/servicesrc/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
    • 同文件存在多个副作用块(watchwatchEffect、生命周期);
    • 一个大 setup 中混合了业务状态和 UI 状态;
    • 逻辑具备跨页面复用价值。
  • hook 命名统一为 useXxx.ts
  • 放置规则:
    • 全局/通用逻辑 -> src/hooks/
    • 模块专属逻辑 -> src/views/<module>/hooks/
  • hook 输出保持最小且清晰:statecomputedactions
  • 页面私有 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.tsrequest.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.tsuse-xxx 不使用,hook 文件仍保持 useXxx.ts)。
  • Vue 组件文件名使用 PascalCase.vue(如 UserProfileCard.vue);页面入口保持 index.vue
  • class、type、interface、enum 名称使用 PascalCase
  • 变量、函数、方法使用 camelCase;常量使用 UPPER_SNAKE_CASE
  • 布尔变量必须带语义前缀:is/has/can/should(如 isLoadinghasPaid)。
  • 事件处理函数统一 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 正确消费。
    • 无新增宽泛 anyunknown 已做类型收窄。
    • props / emits / defineExpose 类型完整,无隐式 any。
    • 全局类型(如 Types.*)引用路径与 src/typings 实际声明一致。
  4. 行为闭环
    • 异步动作 loading 开关成对,error/empty 兜底到位。
    • hook 内定时器、watch 句柄、事件监听、订阅在 onUnmounted 释放。
    • 模板引用的 ref/computed 都有定义且被使用,无孤儿。
  5. 范围纪律
    • 改动未扩散到无关文件。
    • 历史已有问题未顺手"乱改";如确需顺带修复,单独列在"附带修复"区块。
    • 输出区块内显式列出"已校验文件清单"和"未校验/不在范围"的文件。

输出格式(强制)

完工时必须附以下区块:

### 闭环校验结果
**已校验文件**
- 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)。

参考资料