first commit

This commit is contained in:
xiang
2026-07-25 23:45:09 +08:00
commit c7bd957e6f
47 changed files with 3870 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
---
name: vue-coding-style
description: 面向 Vue 前端 Vibe Coding 的工程化编写、修改、修复、重构、审查与验证规范。凡任务涉及 Vue 3、Vue SFC、Composition API、script setup、TypeScript、Vue Router、Pinia、Vite、Vitest、组件、页面、composable、store、前端接口层、样式、可访问性、性能、测试或 Vue 代码 Review 时使用。默认采用主流 Vue 3 + TypeScript 技术路线,但必须先服从项目现有版本、工具链和局部约定;处理 Vue 2 或 Options API 项目时不得擅自迁移。
---
# Vue Coding Style
## 核心目标
以可维护、类型安全、可测试、可访问和可验证为目标完成 Vue 变更。优先保持项目一致性,再应用本技能的默认规范。只修改本次需求所需内容,禁止借机升级依赖、迁移 API 风格或重排无关代码。
## 开始任何 Vue 任务
1. 读取作用域内的 `AGENTS.md`、项目说明和用户提供的需求文档。
2. 检查 `package.json`、锁文件、Vue/Vite/TypeScript 版本、`tsconfig*`、ESLint/格式化配置、测试配置和可用脚本。
3. 阅读目标文件、直接调用方、相邻同类实现、共享类型、路由、store、API 层和相关测试。
4. 区分以下内容:
- 用户明确要求;
- 当前代码与配置事实;
- 本技能给出的默认建议。
5. 明确行为边界、非目标、兼容性、风险和可执行的验收方式;存在通用 Vibe Coding 治理 skill 时,同时遵守其分级与门禁。
6. 在未核实依赖版本前,不使用较新宏、实验性 API 或废弃特性。
## 规则优先级
按以下顺序处理冲突:
```text
用户本次明确要求
→ 已确认的需求与验收标准
→ 作用域最近的项目指令
→ 项目配置、自动化规则和相邻代码惯例
→ 本技能的 Vue 默认规范
→ 个人偏好
```
若更高优先级规则会引入明显缺陷、安全问题或不可验证行为,先暴露冲突和影响,再请求用户决策。不得静默绕过项目规则。
## 按任务加载参考
- 每次编写或修改 Vue 代码,读取 [references/core-standards.md](references/core-standards.md)。
- 涉及目录、组件边界、composable、Pinia、Router、API 或 SSR,读取 [references/architecture.md](references/architecture.md)。
- 涉及实现、修复、测试、性能、安全、可访问性或交付,读取 [references/quality-gates.md](references/quality-gates.md)。
- 编写新组件、composable、store 或测试且需要范式时,读取 [references/examples.md](references/examples.md)。
- 执行代码 Review 或完成交付前,读取 [references/review-checklist.md](references/review-checklist.md)。
只加载当前任务需要的参考文件,但交付前必须执行 Review 清单。
## 默认技术基线
仅在新项目或项目没有相反约定时采用:
- 使用 Vue 3、Single-File Components、Composition API、`<script setup lang="ts">`
- 使用严格 TypeScript,并以 `vue-tsc` 覆盖 SFC 与模板类型检查。
- 使用 Vite 及项目已选择的包管理器。
- 使用 Vue Router 管理客户端路由,使用 Pinia 管理确有跨页面或跨组件共享价值的客户端状态。
- 使用 ESLint、`eslint-plugin-vue` 和项目既有格式化方案;新配置优先 Flat Config。
- 使用 Vitest 做单元/组件测试,使用 `@vue/test-utils` 测试 Vue 组件;关键用户路径按项目选择执行 E2E。
不要因默认基线而改造成熟的 Vue 2、Options API、Nuxt、JS-only 或其他既有项目。除非用户明确授权迁移,否则在既有风格内完成最小改动。
## 不可妥协的实现约束
- 保持单向数据流:props 向下、事件向上;禁止修改 props。
- 为组件边界提供明确的 props、emits、slots 和 exposed API;避免把内部实现暴露给父组件。
- 让模板保持声明式;将复杂派生、分支和数据转换移入 `computed`、函数或 composable。
-`computed` 保持纯净;仅用 `watch`/`watchEffect` 处理副作用,并清理定时器、监听器、请求和过期异步结果。
- 使用稳定且唯一的 `key`;禁止以数组索引标识可增删、排序或筛选的列表项。
- 优先局部状态,其次复用 composable,最后才使用 Pinia;不得把所有状态全局化。
- 在请求边界区分加载、空数据、失败和成功状态;不得吞掉异常或只在控制台记录面向用户的失败。
- 禁止用 `any`、无依据的类型断言、`@ts-ignore` 或 non-null assertion 掩盖可建模问题。
- 优先原生语义 HTML;保证键盘操作、焦点、表单标签和必要的 ARIA 语义。
- 禁止把密钥放入客户端代码或 `VITE_*` 环境变量;谨慎使用 `v-html`,未经可信净化不得渲染外部 HTML。
- 不为假设的未来需求提前建立抽象;出现第二个真实复用点并且语义稳定时再提取。
- 不改变依赖、构建配置、公共 API 或全局样式,除非它们在确认范围内。
## 实施流程
1. 将验收行为写成可观察结果,优先补充或更新能失败的测试。
2. 选择最小改动面,复用项目现有组件、composable、类型、tokens 和工具函数。
3. 先建立数据与类型边界,再实现状态与行为,最后连接模板和样式。
4. 同时处理 loading、empty、error、disabled、权限不足和异步竞态等适用状态。
5. 检查窄视口、键盘、焦点、文本溢出和主题变量等界面边界。
6. 自查变更是否引入无关格式化、重复抽象、死代码、调试输出或不必要依赖。
7. 运行项目已有验证命令;根据锁文件使用对应包管理器,不得混用。
## 验证与交付
优先运行项目现有脚本,并按风险选择:
```text
定向测试
→ lint
→ vue-tsc / typecheck
→ 完整单元或组件测试
→ build
→ 关键页面或 E2E 冒烟
```
对小改动可以只执行受影响范围与必要静态检查;对路由、共享状态、公共组件、构建配置或安全相关改动扩大回归范围。只报告实际执行的命令和结果;无法运行的检查标记为“未验证”并说明原因。
交付时说明:
- 实际行为变化和主要文件;
- 关键设计选择及其项目依据;
- 已执行的测试、类型检查、lint、构建或人工冒烟;
- 未验证项、兼容性风险和剩余问题。
+5
View File
@@ -0,0 +1,5 @@
interface:
display_name: "Vue Coding Style"
short_description: "Vue 3 与 TypeScript 前端编写、重构和审查规范"
default_prompt: "Use $vue-coding-style to implement or review this Vue frontend change with project-aligned conventions and verifiable quality gates."
+107
View File
@@ -0,0 +1,107 @@
# Vue 架构与职责边界
## 目录
- 结构原则
- 组件边界
- Composable
- Pinia
- Vue Router
- API 与数据边界
- 表单
- SSR 与 Hydration
- 依赖和公共 API
- 官方依据
## 结构原则
- 先遵循现有目录结构;不要在单个需求中引入第二套架构。
- 新建中大型项目或独立业务域时,优先按 feature/domain 聚合页面、组件、composable、store、服务和类型;将真正跨域的稳定能力放入 `shared`
- 避免把 `components/``utils/``types/` 变成无边界仓库。目录名需表达所属业务或技术职责。
- 只允许单向依赖:应用层可依赖共享层,共享层不得反向依赖具体页面或业务域。
- 在浏览器、服务端、存储、网络和第三方 SDK 边界建立适配层,避免组件直接承载协议细节。
## 组件边界
- 让页面组件负责路由级编排,让业务组件负责明确用例,让基础组件负责可复用交互和展示。
- 组件同时承担数据请求、复杂业务计算、多块无关 UI 和跨域副作用时,按职责拆分。
- 不按行数机械拆分;仅在职责、复用、测试隔离或性能边界更清晰时提取。
- 保持公共组件 API 小而稳定。避免大量布尔 props 组合出互斥模式;使用判别联合、variant 或具名 slots。
- 不让父组件通过 DOM 查询或 template ref 修改子组件内部状态。
- 向下传递少量明确数据;深层跨树依赖使用 provide/inject 时定义 typed injection key,并提供缺失依赖处理。
- 设计组件时覆盖默认、loading、empty、error、disabled 和只读等适用状态。
## Composable
- composable 封装可复用的有状态逻辑、生命周期或外部系统连接,不把纯函数包装成 composable。
- 接收明确参数或响应式输入;不要隐式读取任意全局单例,除非该依赖就是契约的一部分。
- 返回稳定、最小的公开 API。将 state 与操作区分清楚,必要时返回 readonly state。
- 由创建资源的一方负责释放资源;在内部注册 cleanup,或向调用方暴露明确 `stop`/`dispose`
- 避免一个 composable 同时处理请求、表单、路由、通知和持久化;按用例编排更小能力。
- 对有宿主组件依赖的 composable 标明生命周期约束;纯响应式 composable 应可直接单测。
## Pinia
- 仅将跨组件、跨页面、需持久化或需 DevTools 跟踪的状态放入 store。
- 每个 store 表达一个稳定业务域,使用唯一 id,并在独立文件定义。
- state 保持最小;派生值使用 getters/computed;状态改变和业务动作通过 actions 或明确方法完成。
- 不在组件外随意解构 store;在组件中用 `storeToRefs` 解构响应式 state/gettersactions 可直接解构。
- setup store 必须返回所有应被 Pinia 管理的 state;不要依赖“私有 state”技巧破坏 SSR、DevTools 或插件行为。
- 不在 store 中保存 Router/Route、DOM 元素或不可序列化第三方实例;这些依赖留在调用层或适配层。
- 持久化仅保存必要字段,并处理版本迁移、过期、隐私与反序列化失败。
- 测试 store 的公开 action、getter 和状态转移,不依赖内部实现顺序。
## Vue Router
- 页面级组件使用动态 import 做路由懒加载;不要把异步组件与路由懒加载混为一谈。
- 优先命名路由,集中定义 path、name 和 meta 类型。
- 将认证、权限和通用重定向放在合适的全局或路由级 guard;不要把所有页面业务塞入全局 guard。
- guard 返回导航结果或抛出可处理错误;避免多次调用导航、隐式 fall-through 和未处理竞态。
- 客户端路由权限只控制体验,不构成安全边界;后端必须独立授权。
- 路由参数和 query 都是不可信字符串输入,使用前解析、校验并处理缺失或重复值。
- 编程式导航需要时等待结果并处理 navigation failure。
## API 与数据边界
- 将 HTTP 客户端配置、认证头、错误归一化和序列化放入 API 基础层。
- 按业务域定义 service/repository 函数,组件不直接拼接重复 URL 或处理底层响应结构。
- 为请求参数和响应 DTO 提供类型;对不可信响应在运行时校验关键字段。
- 在边界将 DTO 映射为前端领域模型,避免蛇形命名、nullable 约定和后端枚举扩散到整个 UI。
- 支持请求取消或过期响应丢弃;切页、快速搜索和重复提交必须处理竞态。
- 对 mutation 提供重复提交保护、幂等策略或清晰恢复方式。
- 不在客户端持有服务端密钥。`VITE_*` 内容会进入客户端 bundle,只能存放公开配置。
- 将用户可见错误转换为可行动信息,同时保留脱敏的诊断上下文。
## 表单
- 分离原始 DTO、可编辑表单值、验证结果和提交 payload。
- 明确字段 touched、dirty、validating、disabled 和 submitting 状态。
- 客户端验证用于即时反馈,服务端仍是最终业务校验来源。
- 服务端字段错误映射回对应控件,并提供表单级错误摘要或焦点移动。
- 避免 watcher 组成不可追踪的双向同步链;建立单一数据源和显式 reset/submit 流程。
- 离开页面可能丢失编辑内容时,按需求提供确认或草稿策略。
## SSR 与 Hydration
- 先识别项目是否为 Nuxt、SSR、SSG 或纯 SPA,再选择数据获取和生命周期位置。
- 不在服务端共享请求级可变单例,避免用户间状态泄漏。
- 将浏览器 API 放在客户端生命周期或带环境判断的适配层。
- 保证服务端与首次客户端渲染结构一致;时间、随机数、locale 和设备判断需避免 hydration mismatch。
- setup store、持久化插件和请求缓存必须符合项目 SSR 约定。
## 依赖和公共 API
- 新增依赖前证明现有能力不足,并评估体积、维护状态、类型、许可证、SSR 和 tree-shaking。
- 不因单个小功能引入大型工具库。
- 公共组件、composable、store 和类型一旦被多个模块使用,即视为契约;修改时检查全部调用方并补充回归测试。
- 删除或重命名公共导出前,先迁移调用方并确认兼容策略。
## 官方依据
- [Vue Composables](https://vuejs.org/guide/reusability/composables.html)
- [Vue 状态管理](https://vuejs.org/guide/scaling-up/state-management.html)
- [Pinia:定义 Store](https://pinia.vuejs.org/core-concepts/)
- [Vue Router:懒加载路由](https://router.vuejs.org/guide/advanced/lazy-loading.html)
- [Vue Router:导航守卫](https://router.vuejs.org/guide/advanced/navigation-guards.html)
- [Vite 环境变量与模式](https://vite.dev/guide/env-and-mode)
@@ -0,0 +1,146 @@
# Vue 核心代码规范
## 目录
- 技术风格与版本适配
- 命名与文件
- SFC 结构
- Props、Emits、Slots 与组件 API
- 响应式状态
- 模板
- TypeScript
- 异步与副作用
- 样式
- 注释与日志
- 官方依据
## 技术风格与版本适配
- 新建现代 Vue 3 SFC 时,优先使用 Composition API 与 `<script setup lang="ts">`
- 修改既有文件时,保持该模块的 API 风格;不要在一次无关功能变更中混合 Options API 和 Composition API。
- 使用 `defineModel`、响应式 props 解构、`useTemplateRef` 等能力前,核实项目 Vue 版本与团队惯例。
- 不使用实验性功能完成核心业务,除非项目已采用且存在回退或验证方案。
## 命名与文件
- 使用多单词组件名,避免与原生 HTML 元素冲突。
- 组件文件使用 `PascalCase.vue`,例如 `UserProfileCard.vue`
- 页面按项目约定使用 `*View.vue``*Page.vue`,不要在同一目录混用。
- composable 使用 `useXxx.ts`,并导出同名 `useXxx` 函数。
- Pinia store 使用 `useXxxStore`,每个 store 独立文件。
- 布尔变量使用 `is``has``can``should` 等前缀。
- 事件处理函数使用 `handleXxx`;传给 props 的回调可按项目约定使用 `onXxx`
- 类型、接口和泛型使用 `PascalCase`;不要默认增加 `I` 前缀。
- 常量使用项目既有约定;仅真正跨模块、不可变的常量使用全大写形式。
- 测试文件放在目标代码旁或项目统一测试目录,命名保持 `.spec.ts``.test.ts` 的既有选择。
## SFC 结构
默认保持以下顶层顺序:
```vue
<script setup lang="ts">
// imports
// types and component contracts
// injected dependencies, router and stores
// state
// computed values
// watchers and lifecycle
// handlers
</script>
<template>
<!-- declarative UI -->
</template>
<style scoped>
/* component-owned styles */
</style>
```
- 服从项目的 block 顺序和 import 排序规则。
- 仅在模板或组件逻辑实际需要时创建变量;不要为了分区制造空注释。
- 组件私有样式优先 `scoped` 或项目 CSS Modules 方案;主题、reset、tokens 和工具类保持全局。
- 避免使用 `:deep()` 穿透第三方或子组件内部;无法通过公开 API 定制时,记录耦合原因。
## Props、Emits、Slots 与组件 API
- 用类型声明组件契约,为可选 props 明确默认行为。
- props 名表示数据或状态;emits 名表示已发生的业务动作,例如 `submit``update:modelValue`
- 不以 emit 冒充命令式调用;父组件需要控制子组件时,优先 props 和状态驱动。
- 仅在确有命令式能力时使用 `defineExpose`,并保持暴露面最小。
- 为有双向语义的表单值使用 `v-model`/`defineModel`;普通业务数据继续使用 props + emits。
- 不直接解构会丢失响应性的对象。解构 Pinia 状态时使用 `storeToRefs`;解构一般响应式对象时按需使用 `toRefs`
- 为 slots 定义清晰语义;不要让父组件依赖子组件内部 DOM 结构。
- 可变对象或数组默认值必须保证实例隔离,并符合当前 Vue 版本支持的写法。
## 响应式状态
- 对基本类型和可整体替换的值使用 `ref`
- 对语义内聚且主要按属性更新的对象使用 `reactive`;不要创建巨大、无边界的响应式对象。
- 使用 `computed` 表达派生值,不将可推导数据复制进 state。
- 不在 `computed` 中发送请求、写 store、修改其他 state 或触发日志等副作用。
- 监听明确来源,避免无边界 deep watch;大型对象优先监听必要字段或规范化后的签名。
- 在 watcher 中处理异步时,取消或忽略过期结果,防止旧请求覆盖新状态。
- 对第三方实例、大型不可变结构或不需要深层代理的数据,评估 `shallowRef`/`markRaw`,但先用性能证据证明需要。
- composable 返回值需保持稳定且易解构;需要保护写入口时向外暴露 readonly state 与显式 mutation 函数。
## 模板
- 保持模板表达式简短且无副作用。
-`v-for` 提供稳定业务键;只有完全静态、永不重排的展示列表才可评估使用索引。
- 不在同一元素上组合 `v-if``v-for`;先计算筛选结果或使用外层 `<template>`
- 频繁切换且初始渲染可接受时评估 `v-show`;条件低频或初始成本高时使用 `v-if`
- 原生交互使用原生元素:动作使用 `<button>`,导航使用 `<a>`/`RouterLink`,不要用可点击 `<div>` 代替。
- 表单控件提供关联的 `<label>`、名称、错误提示和 disabled/loading 状态。
- 仅在确有 HTML 内容需求且输入经过可信净化时使用 `v-html`
- 对异步列表和页面显式渲染 loading、empty、error 和 success 状态。
- 使用 CSS 处理纯展示状态;不要用 JavaScript 复制 CSS 已能表达的响应式布局。
## TypeScript
- 遵守项目 `strict` 配置;新代码不得主动弱化类型检查。
- 优先类型推断,仅在公共边界、复杂返回值和可能误推断处显式标注。
- 使用 `unknown` 接收不可信值,并通过 schema、type guard 或判别字段收窄。
- 使用 `import type` 引入纯类型,避免无意义运行时依赖。
- 在 API、路由参数、storage、postMessage 等外部边界做运行时校验;TypeScript 类型不能验证运行时数据。
- 区分传输 DTO、领域模型和表单模型;存在语义差异时显式映射。
- 优先判别联合表达有限状态,避免多个互相矛盾的布尔值。
- 本地封闭集合优先联合类型或 `as const` 对象;是否使用 `enum` 服从项目约定。
- 避免 `as``!``@ts-ignore`。若第三方类型确有缺陷,局部封装并说明依据。
- 为泛型设置能表达用途的约束,不创建只为“显得通用”的泛型。
## 异步与副作用
- 让异步动作表达 loading、error 和 retry/恢复策略。
- 使用 `try/catch/finally` 保证状态复位;不要将所有异常统一转换为无信息的“失败”。
- 区分可展示业务错误、认证/权限错误、网络错误和程序缺陷。
- 搜索、联想和切页请求处理取消、去抖或响应顺序,防止竞态。
-`onUnmounted` 或 watcher cleanup 中释放事件监听、observer、timer、subscription 和请求。
- 不在模块顶层执行依赖浏览器、用户或实例生命周期的副作用。
- 对 SSR 项目避免在服务端路径直接访问 `window``document``localStorage`
## 样式
- 优先复用项目设计 tokens、组件库变量和现有布局原语。
- 不硬编码已有 token 表达的颜色、层级、间距、圆角或字号。
- 类名遵循项目既有 BEM、utility、CSS Modules 或其他约定。
- 避免高特异性、`!important` 和依赖 DOM 深度的选择器。
- 同时验证窄视口、文本放大、长内容、空内容和交互焦点。
- 尊重 `prefers-reduced-motion`,动画不应阻止操作或传达唯一信息。
## 注释与日志
- 注释解释“为什么、约束和权衡”,不要复述代码。
- 为公共 composable、复杂协议和非直观兼容性补充必要说明。
- 删除调试日志和断点。生产日志不得包含 token、个人信息、完整请求体或敏感业务数据。
- TODO 必须包含可操作条件或关联任务;不要留下无主 TODO。
## 官方依据
- [Vue 官方风格指南](https://vuejs.org/style-guide/)
- [Vue `<script setup>`](https://vuejs.org/api/sfc-script-setup.html)
- [Vue Composition API 与 TypeScript](https://vuejs.org/guide/typescript/composition-api)
- [Vue TypeScript 总览](https://vuejs.org/guide/typescript/overview)
- [`eslint-plugin-vue` 规则](https://eslint.vuejs.org/rules/)
+181
View File
@@ -0,0 +1,181 @@
# Vue 代码范式
仅在项目版本和既有约定支持时使用以下范式。复制前替换领域名称、错误处理和组件库 API。
## 类型安全组件契约
```vue
<script setup lang="ts">
import { computed } from 'vue'
interface UserSummary {
id: string
displayName: string
}
interface Props {
user: UserSummary
selected?: boolean
}
const props = withDefaults(defineProps<Props>(), {
selected: false,
})
const emit = defineEmits<{
select: [userId: string]
}>()
const accessibleName = computed(() => `选择用户 ${props.user.displayName}`)
function handleSelect(): void {
emit('select', props.user.id)
}
</script>
<template>
<button
type="button"
:aria-pressed="selected"
:aria-label="accessibleName"
@click="handleSelect"
>
{{ user.displayName }}
</button>
</template>
```
要点:
- 将 props 和 emits 作为公共契约。
- 使用稳定业务 id 发出事件,不把完整可变内部对象泄露出去。
- 使用原生 button 获得键盘和语义能力。
- 若项目 Vue 版本支持且团队采用响应式 props 解构,可用等价的解构默认值写法。
## 带竞态保护的 composable
```ts
import { readonly, ref, toValue, watch, type MaybeRefOrGetter } from 'vue'
interface User {
id: string
name: string
}
interface UserService {
getUser(userId: string, signal: AbortSignal): Promise<User>
}
export function useUser(
userId: MaybeRefOrGetter<string>,
service: UserService,
) {
const data = ref<User | null>(null)
const error = ref<Error | null>(null)
const isLoading = ref(false)
watch(
() => toValue(userId),
async (currentUserId, _previousUserId, onCleanup) => {
const controller = new AbortController()
onCleanup(() => controller.abort())
isLoading.value = true
error.value = null
try {
data.value = await service.getUser(currentUserId, controller.signal)
} catch (cause: unknown) {
if (!controller.signal.aborted) {
error.value = cause instanceof Error
? cause
: new Error('Unknown user loading error')
}
} finally {
if (!controller.signal.aborted) {
isLoading.value = false
}
}
},
{ immediate: true },
)
return {
data: readonly(data),
error: readonly(error),
isLoading: readonly(isLoading),
}
}
```
要点:
- 通过依赖参数隔离网络边界,便于测试。
- 对参数变化取消旧请求,避免过期结果覆盖新状态。
- 只暴露 readonly 状态。
- 实际项目应使用已有错误归一化,不重复定义通用错误层。
## 最小 Pinia Store
```ts
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', () => {
const itemIds = ref<string[]>([])
const itemCount = computed(() => itemIds.value.length)
function addItem(itemId: string): void {
if (!itemIds.value.includes(itemId)) {
itemIds.value.push(itemId)
}
}
function clear(): void {
itemIds.value = []
}
return {
itemIds,
itemCount,
addItem,
clear,
}
})
```
要点:
- 只保存不可推导的最小 state。
- 用 computed 表达 getter。
- 通过 action 表达状态变化。
- 真实 store 若执行请求,需补齐 loading、error、并发与恢复策略。
## 行为导向组件测试
```ts
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import UserSelectButton from './UserSelectButton.vue'
describe('UserSelectButton', () => {
it('emits the selected user id when activated', async () => {
const wrapper = mount(UserSelectButton, {
props: {
user: { id: 'user-42', displayName: 'Ada' },
},
})
await wrapper.get('button').trigger('click')
expect(wrapper.emitted('select')).toEqual([['user-42']])
})
})
```
要点:
- 通过用户可操作元素交互。
- 验证公开事件和业务结果,不访问组件内部 ref。
- 对无障碍名称、loading、disabled 和 error 等关键状态按变更补充断言。
@@ -0,0 +1,108 @@
# Vue 质量门禁
## 目录
- 工具链
- 测试策略
- 安全
- 可访问性
- 性能
- 兼容性与韧性
- 验证顺序
- 官方依据
## 工具链
- 使用项目锁文件对应的包管理器和锁定版本,不生成第二种锁文件。
- 先使用 `package.json` 已有脚本;不要猜测脚本名或绕开项目封装。
- 对 Vue SFC 执行 `vue-tsc` 或项目等价类型检查,因为 Vite 的转译本身不进行完整类型检查。
- 使用项目既有 ESLint 和格式化配置。新项目优先 ESLint Flat Config、`eslint-plugin-vue` 推荐配置及与 TypeScript 匹配的规则集。
- 将格式交给自动化工具,把人工 Review 集中在行为、边界、类型和可维护性。
- 不在功能变更中顺便升级 lint 规则或全仓格式化。
## 测试策略
按风险分配测试:
- 纯函数、数据映射、validators:单元测试。
- composable:测试输入、状态转移、副作用和 cleanup;有组件生命周期依赖时通过宿主组件挂载。
- Pinia store:测试 actions、getters、状态转移和失败恢复。
- Vue 组件:以 props、emits、slots、可见 DOM 和用户交互等公开接口为中心。
- 页面和跨模块流程:组件集成测试或 E2E。
- 样式、原生事件、浏览器 API、cookie、storage 和网络失败:使用真实浏览器环境验证关键路径。
遵守以下规则:
- 优先测试用户可观察行为,不测试私有 ref、内部方法调用顺序或 CSS 实现细节。
- 覆盖 success、loading、empty、error、disabled、边界值和竞态中与变更相关的状态。
- 不只依赖 snapshot;为关键结果写有意义的断言。
- 只 mock 稳定边界,例如网络、时间或第三方 SDK;不要把所有子组件都 stub 掉而失去集成价值。
- 修复缺陷时先添加可复现失败的回归测试;无法自动化时记录人工复现和验证步骤。
- 覆盖率用于发现盲区,不用单一百分比替代风险判断。新改动的关键分支必须有证据。
## 安全
- 将模板插值作为默认文本输出方式;未经可信 sanitizer 处理,不渲染外部或用户提供的 HTML。
- 不把用户输入直接拼接到 URL、style、动态组件、下载文件名或 DOM API;按上下文校验与编码。
- 将认证和授权建立在后端;前端 guard、隐藏按钮和 store 状态只改善体验。
- 不把 secrets、私钥、服务账号、数据库凭据或长期 token 放入仓库、bundle、localStorage 或 `VITE_*`
- 对 access token 的存储和刷新遵循项目安全架构,不自行发明持久化方案。
- 外部链接、重定向和回调地址使用 allowlist 或严格校验,防止开放重定向。
- 对敏感操作处理 CSRF、重放、重复提交和权限失败,具体机制由后端契约决定。
- 日志、错误提示、埋点和测试夹具必须脱敏。
- 依赖升级和安全修复必须独立评估兼容性并运行回归,不把审计告警机械等同于可利用漏洞。
## 可访问性
- 优先使用原生语义元素,只有原生语义不足时才增加 ARIA。
- 所有交互可用键盘完成,并有可见焦点。
- 图标按钮提供可访问名称;装饰图标从辅助技术隐藏。
- 表单控件关联 label,错误消息可被识别,必填和 invalid 状态不只依赖颜色。
- 弹窗、抽屉和菜单管理初始焦点、焦点陷阱、Escape 和关闭后的焦点恢复。
- 路由切换按产品结构更新标题、主标题或焦点,使屏幕阅读器用户感知页面变化。
- 保持颜色对比、文本缩放、触控目标和减少动画支持。
- 动态状态变化需要时使用合适 live region,但避免过度播报。
## 性能
- 先测量再优化,区分首屏加载与交互更新性能。
- 页面组件使用路由级代码分割;大型可选功能和低频组件按需求异步加载。
- 选择可 tree-shake 的导入,避免引入完整工具库只使用一个函数。
- 保持 props 稳定,减少不必要的子树更新;不要滥用 `v-memo``v-once` 或手工缓存。
- 大型列表评估虚拟滚动;大型不可变数据评估浅层响应式。
- 避免过度组件抽象导致额外实例和更新层级,尤其在大列表内。
- 图片设置合理尺寸、格式和懒加载策略,防止布局偏移。
- 请求去重、缓存和预取必须有失效规则,不能以陈旧数据换取表面速度。
- 将 bundle 体积变化纳入新增依赖或大功能的验证。
## 兼容性与韧性
- 按项目 `browserslist`、目标运行时和 SSR 环境验证,不假设仅运行在最新 Chrome。
- 对网络慢、离线、超时、重复点击、刷新、返回、并发导航和权限过期处理适用状态。
- 清理组件卸载后的回调,防止内存泄漏和卸载后更新。
- 全局错误边界不得吞掉错误;提供用户恢复路径并保留脱敏诊断。
- 破坏性 UI 或数据操作需要明确确认、撤销或恢复策略。
## 验证顺序
先读取脚本,再按变更风险选择命令:
1. 运行最相关的单个或少量测试。
2. 运行 lint。
3. 运行 `vue-tsc`/typecheck。
4. 运行相关测试套件或覆盖率。
5. 运行 production build。
6. 对关键用户路径执行浏览器或 E2E 冒烟。
若修改共享组件、路由、store、请求基础层、构建配置或公共类型,扩大到所有受影响调用方。任何跳过项都必须标为未验证,不得推测通过。
## 官方依据
- [Vue 测试指南](https://vuejs.org/guide/scaling-up/testing.html)
- [Vue 工具链](https://vuejs.org/guide/scaling-up/tooling.html)
- [Vitest Coverage](https://vitest.dev/guide/coverage.html)
- [Vue 安全最佳实践](https://vuejs.org/guide/best-practices/security.html)
- [Vue 可访问性最佳实践](https://vuejs.org/guide/best-practices/accessibility.html)
- [Vue 性能最佳实践](https://vuejs.org/guide/best-practices/performance.html)
- [Vite 环境变量与安全提示](https://vite.dev/guide/env-and-mode)
@@ -0,0 +1,89 @@
# Vue Review 与交付清单
按变更风险使用清单。只勾选实际检查的项目,并将问题按严重性优先报告。
## 需求与范围
- [ ] 实现与用户要求和验收标准一致。
- [ ] 未混入依赖升级、API 迁移、全仓格式化或无关重构。
- [ ] 已识别所有直接调用方、共享契约和回归范围。
- [ ] 新抽象来自真实复用或清晰职责,而非假设需求。
## Vue 与组件
- [ ] API 风格和 Vue 版本与项目一致。
- [ ] 组件名为多单词,文件和符号命名符合项目约定。
- [ ] props、emits、slots、`v-model` 与 exposed API 明确且最小。
- [ ] 未修改 props,未破坏单向数据流。
- [ ] 模板简洁、无副作用,复杂逻辑已移出模板。
- [ ] `v-for` key 稳定,未在同一元素混用 `v-if``v-for`
- [ ] `computed` 纯净,watcher 有明确来源并清理副作用。
- [ ] loading、empty、error、success、disabled 等适用状态完整。
## TypeScript 与数据
- [ ] 未用 `any`、无依据断言、`@ts-ignore``!` 掩盖问题。
- [ ] 外部数据、路由参数、storage 和环境变量已解析或校验。
- [ ] DTO、领域模型和表单模型在需要时已分离。
- [ ] 异步请求处理失败、复位、取消或过期响应。
- [ ] 公共类型和 API 变更已检查全部调用方。
## 状态、路由与架构
- [ ] 状态处于最小合理层级,没有不必要地放入 Pinia。
- [ ] store state 最小、getter 纯净、action 边界清晰。
- [ ] composable 依赖明确、返回面最小、资源可释放。
- [ ] 页面路由按需懒加载,guard 没有承载不相关业务。
- [ ] 前端权限控制未被误当成服务端授权。
- [ ] 组件、store 与页面未直接复制底层 API 协议逻辑。
## 安全与隐私
- [ ] 未将 secrets 或敏感 token 放入客户端代码、日志、测试或 `VITE_*`
- [ ] 未渲染未经可信净化的外部 HTML。
- [ ] URL、重定向、动态资源和用户输入按上下文处理。
- [ ] 错误、日志、埋点和测试数据已脱敏。
## 可访问性与界面
- [ ] 使用正确的原生语义元素。
- [ ] 所有交互可键盘操作并有可见焦点。
- [ ] 表单控件有 label,错误和状态不只依赖颜色。
- [ ] 图标按钮、弹窗、动态通知和路由切换有适当可访问语义。
- [ ] 已考虑窄视口、长文本、缩放、主题和减少动画。
## 性能与生命周期
- [ ] 没有无证据的过早优化或滥用缓存。
- [ ] 页面级大模块按需加载,新增依赖的 bundle 影响合理。
- [ ] 大列表、大对象或高频更新路径已按风险检查。
- [ ] timer、listener、observer、subscription 和请求在适当时机清理。
- [ ] SSR 项目没有 hydration 不一致或跨请求状态污染。
## 测试与验证
- [ ] 缺陷修复有回归证据。
- [ ] 测试关注公开行为,不耦合内部实现。
- [ ] 关键成功、失败、空、加载和边界分支有相称覆盖。
- [ ] 已运行适用的定向测试、lint、typecheck、测试套件、build 或 E2E。
- [ ] 未运行或失败的检查已准确披露,不推测通过。
## Review 输出格式
先报告问题,再给摘要。每个问题包含:
```text
严重性 + 文件与行号
→ 可触发的具体场景
→ 对用户或系统的影响
→ 最小修复方向
```
严重性建议:
- P0:可造成安全事故、数据破坏或系统不可用,必须立即阻断。
- P1:常见路径中的明确功能错误、权限问题或严重回归,合并前修复。
- P2:边界条件缺陷、可维护性风险或缺失的重要测试,应尽快修复。
- P3:低风险一致性、可读性或非阻断改进。
若未发现可操作问题,明确说明“未发现问题”,并列出已检查范围与仍未验证的风险。