Note
vue-code-standards
vue项目代码规范
发布于 更新于
---
name: vue-code-standards
description: 在生成、修改、重构或审查 Vue 项目代码时应用统一代码规范,覆盖目录划分、文件与变量命名、TypeScript、Vue 组件、注释、接口请求及样式。适用于 Vue 页面、组件、composable、Store 和前端 API 模块开发,默认面向 Vue 3 + TypeScript + Vite。用于规范代码实现,不用于生成代码规范说明文档;纯概念问答不触发。
---Vue 项目代码规范
执行方式
- 修改前读取适用的项目指令、
package.json、现有目录、格式与类型配置,以及邻近同类代码。确认 Vue 版本、包管理器、请求封装、路由、状态管理和组件库。 - 遵守用户当前要求和项目明确规定;未约定的部分采用本规范。存在不同风格但无明确规定时,新模块使用本规范,局部修改保持邻近代码一致,不附带全项目迁移。
- 新建 Vue 3 组件默认使用 TypeScript 和
<script setup lang="ts">。修改已有 Vue 2、JavaScript 或 Options API 文件时保持兼容,不擅自升级、改写架构或新增依赖。 - 只创建当前任务需要的目录与文件。复用已有组件、类型、工具和请求封装,不为目录完整而生成空壳,不把示例业务当作产品需求。
- 完成实现后检查变更范围内的规范,并运行适用的现有检查。将变更说明、取舍和验证结果留在回复中,不写入业务代码或新建说明文档,除非用户要求。
目录划分
项目无既定结构时采用以下默认路径;orders 仅作为业务命名示例,替换为实际业务名。
| 路径 | 放置内容 |
|---|---|
public/ |
需要固定名称或固定地址访问的静态资源 |
src/assets/images/、src/assets/icons/ |
通过构建工具引用的图片和图标 |
src/components/base/ |
跨业务基础组件,不依赖业务 API 或业务 Store |
src/components/business/ |
跨页面复用的业务组件 |
src/layouts/ |
页面布局壳 |
src/views/orders/ |
对应业务的路由页面 |
src/views/orders/components/ |
该业务页面私有组件 |
src/views/orders/composables/ |
该业务页面私有响应式逻辑 |
src/views/orders/order-view.types.ts |
多个文件共用的页面展示类型 |
src/api/http.ts |
HTTP 实例、通用请求配置与错误转换 |
src/api/order.api.ts |
业务接口函数 |
src/api/order.types.ts |
对应接口的请求与响应类型 |
src/api/common.types.ts |
共享接口响应结构、分页契约 |
src/router/index.ts |
创建路由实例 |
src/router/routes/order.routes.ts |
业务路由定义,路由较少时不必拆分 |
src/router/guards.ts |
路由守卫 |
src/stores/user.store.ts |
跨页面共享状态 |
src/composables/ |
跨业务复用的响应式逻辑 |
src/utils/ |
不依赖 Vue 实例与页面状态的普通工具函数 |
src/constants/ |
多处使用的常量与枚举映射 |
src/types/ |
非接口专属的跨模块共享类型 |
src/styles/ |
全局样式、重置样式、设计变量 |
src/directives/、src/plugins/ |
按需创建的自定义指令、插件初始化 |
src/App.vue、src/main.ts |
根组件与应用初始化入口 |
tests/e2e/ |
已采用端到端测试时的场景测试 |
- 仅被当前文件使用的小函数、类型和常量就近保留;复杂度或真实复用需要时再拆分。
- 页面临时状态保留在页面,不因多个子组件使用就立即放入全局 Store。
- 页面私有模块不被其他页面直接引用;真实复用出现后提升到共享目录。
- 基础组件不得反向依赖业务页面;API 层不得依赖组件与路由;工具函数不得依赖页面或 Store。
- 按职责细分工具文件,如
date.ts、money.ts;不将所有内容堆入common.ts或index.ts。 main.ts只处理初始化,不承载业务流程。避免循环依赖与过量转导出。- 已采用
features/<业务>/的项目延续其结构;只在任务需要时迁移完整业务模块,不同时创建两套平行组织方式。
文件与标识符命名
| 对象 | 规则 | 示例 |
|---|---|---|
| 普通目录 | kebab-case | user-settings/ |
| Vue 组件 | PascalCase,使用多单词语义名 | OrderTable.vue |
| 路由页面 | PascalCase + View | OrderListView.vue |
| 布局组件 | PascalCase + Layout | DefaultLayout.vue |
| 基础组件 | Base + PascalCase | BaseDialog.vue |
| 业务组件 | 业务名 + 职责 | OrderFilter.vue |
| composable 文件及函数 | use + PascalCase | usePagination.ts、usePagination |
| 普通 TS 文件 | kebab-case | format-date.ts、money.ts |
| API / 类型 / Store / 路由模块文件 | 业务名 + 职责后缀 | order.api.ts、order.types.ts、user.store.ts、order.routes.ts |
| 图片、图标、样式文件 | kebab-case | empty-orders.svg、tokens.css |
| 单元与组件测试 | 被测文件主体名 + .test.ts | money.test.ts、OrderTable.test.ts |
| 端到端测试 | 场景名 + .spec.ts | order-create.spec.ts |
| 变量、参数、对象属性 | camelCase | selectedCategoryId |
| 集合 | 复数名词或明确集合含义 | orders、selectedIds |
| 索引映射 | 表明索引关系 | userById |
| 布尔值 | is / has / can / should 开头 | isLoading、canSubmit |
| 函数 | 动词 + 对象 | formatAmount、createOrder |
| UI 事件处理函数 | handle + 行为 | handleSubmit |
| Store 工厂函数 | use + 业务名 + Store | useUserStore |
| 类型、接口、类 | PascalCase | User、CreateOrderBody |
| 模块级固定常量 | UPPER_SNAKE_CASE | DEFAULT_PAGE_SIZE |
| props | camelCase 声明,kebab-case 传入 | pageSize、:page-size |
| 自定义事件 | kebab-case,模型更新遵循框架命名 | submit-success、update:modelValue |
| 路由 name / path | kebab-case | order-detail、/orders/:id |
| CSS 类名 | kebab-case;复杂组件采用 BEM | order-card__title、order-card--selected |
- 保留
App.vue、main.ts、工具配置及框架要求的名称;显式页面名优先于大量index.vue。 - import 路径大小写必须与实际文件一致。仅在提供公开入口时创建
index.ts。 - 不使用拼音、
data1、flag、new2、final等含糊名称;小范围回调可使用item、index。 - 将单位写入可能混淆的名称:
timeoutMs、amountInCents。区分业务日期orderDate与时间点createdAt。 - 不因使用 const 就将变量大写;不强制类型前缀 I / T,也不强制异步函数加 Async。
- 按接口契约保留字段与枚举值;如需字段转换,集中在 API 适配层,不在页面重复转换。
TypeScript 与函数
- 新项目启用 strict;现有项目不为局部任务随意改动编译选项。
- 为公共函数、API 和模块边界提供明确参数与返回类型;局部简单值允许推断。
- 使用
import type导入纯类型。对象结构默认 interface,联合类型默认 type;不机械改写现有类型。 - 未知数据使用 unknown 并缩小类型;不用 any、非空断言、双重断言或
@ts-ignore掩盖问题。 - 类型断言不能代替运行时校验;外部数据按实际风险在边界验证。
- 默认 const,需重新赋值时用 let;不用 var。使用严格相等。
- 区分 null、undefined、空字符串、0 和 false;保留有效零值时使用
??而不是||。 - 函数聚焦单一职责,优先提前返回;避免重复状态、过深嵌套和与任务无关的抽象。
- 明确 await、返回或 catch Promise;
void promise不能代替错误处理。 - 金额、日期、枚举和 ID 遵守后端契约,不擅自更换字段、单位或响应包装。大整数 ID 采用接口约定的字符串,不经 number 中转。
- 金额计算遵循明确精度方案;业务日期不随意转换为 UTC 时间点再截取日期。
Vue 组件
单文件组织
- 新 SFC 使用 script setup → template → style scoped 的顺序。
- 脚本按 import、类型、props / emits、状态、computed、业务函数、watch / 生命周期组织;复杂组件可按完整业务职责聚合相关代码。
- 组件达到可独立命名的职责时拆分,如筛选栏、列表、编辑弹窗;不机械按照行数拆分。
- 优先复用项目组件库,不创建行为相同的重复基础组件。
数据与通信
- 为 props、emits 提供类型;不直接修改 props 及其嵌套对象。通过事件提交变更意图。
- 编辑表单建立独立草稿,按结构处理嵌套引用;不滥用 JSON 序列化充当通用深拷贝。
- 独立状态默认 ref,稳定关联对象可用 reactive;派生状态使用 computed,computed 不产生副作用。
- watch 用于外部同步与副作用;监听属性使用 getter,不默认 deep。
- 不直接解构 reactive 的基本类型字段后期待其继续响应式;需要时使用 toRefs。
- 只有确认安装版本支持时才使用响应式 props 解构、defineModel 等特性;未知版本时使用
props.xxx等兼容方式。 - withDefaults 中可变数组或对象默认值使用工厂函数。
- 生命周期内创建的定时器、事件监听、订阅和连接负责清理;KeepAlive 按业务处理停用状态。
- defineExpose 仅暴露明确需要的命令式能力,常规通信使用 props / emits。
模板
- SFC 内自定义组件使用 PascalCase,原生标签使用小写。
- v-for 提供稳定唯一 key;可增删或排序的列表不用数组下标做 key。
- 不在同一元素混用 v-if 与 v-for;先计算筛选结果或调整容器层级。
- 模板只保留简单表达式,复杂逻辑进入 computed 或函数,不在渲染表达式中产生副作用。
- 非提交按钮写明
type="button";输入关联 label;图标按钮提供可访问名称,图片提供合适的 alt。 - 使用语义化交互元素,保留键盘操作和焦点可见性。
注释
- 使用中文说明、英文标识符,采用 JSDoc 风格。
- 公共函数、API、公共 composable 和复杂业务函数写明用途、参数含义、返回语义;有异常、单位、限制或副作用时一并说明。
- TS 类型由签名提供,注释解释字段语义,不重复维护
{number}等类型;JS 中可用 JSDoc 补充类型。 - 参数及响应字段的详细说明放在对应类型声明旁;API 函数注释引用类型,并说明 HTTP 方法、路径和业务契约。
- 短状态说明优先行尾,computed / watch 的必要说明放上方;长说明及公共类型字段使用上方注释。
- template 的主要业务区域添加说明;简单包装元素不逐个注释。
- CSS 注释说明业务区域、特殊布局与覆盖原因,不给含义清晰的选择器重复写标签。
- 每个新建且支持注释的源文件必须包含文件元数据;修改已有文件时补齐缺失项,具体遵守下方“文件元数据”规则。
- 注释解释原因和约束,不复述语法。移除失效注释与大段被注释掉的代码。
- TODO 写明具体事项与解决条件,不留下无信息的“待优化”。
/**
* 格式化金额显示。
* @param amountInCents - 安全整数金额,单位:分
* @returns 保留两位小数的元金额字符串,不附加货币符号
*/
将这类注释写在实际函数上,按真实实现调整契约,不把注释示例或缺失实现的占位内容写入交付代码。
文件元数据(必须)
- 对每个新建的 Vue、JS、TS、CSS、SCSS、HTML 等支持注释的源文件,以及支持注释的配置文件,添加文件头元数据,统一使用
@author、@date、@description,不得因文件短小而省略。 @author表示文件作者。新建文件默认填写用户指定的作者Brave;用户或项目明确指定其他作者时采用其指定值。修改已有文件时保留原作者,不将其统一替换为 Brave 或 agent 名称。@date表示创建时间,不是最近修改时间。新建文件使用创建时的实际日期与时间,采用带时区偏移的 ISO 8601 格式,例如2026-09-20T15:30:00+08:00。使用项目明确的时区;未指定时使用 UTC 并以Z结尾。读取实际系统时间,不复制示例时间,也不将会话日期冒充准确创建时间。@description使用中文简述文件职责、主要用途或边界,按实际内容填写,不写“这是一个组件”等空泛描述;职责变化时同步更新。- 修改已有文件时检查并补齐元数据,保留已有且有效的创建信息。缺失作者或创建时间时,优先查找可信历史记录;版本控制首次新增记录可作为依据,但重命名、导入或复制的提交时间不得直接冒充创建时间。无法确认时分别填写
未知(历史未记录),不使用当前时间或默认作者伪造历史信息。 - Vue SFC 在
<script setup>或<script>块开头添加一份 JSDoc 文件头;没有 script 的 SFC 在文件顶部使用 HTML 注释,不为文件头额外创建 script。无需在 template 与 style 中重复整份元数据。 - JS、TS 使用 JSDoc;CSS、SCSS 使用块注释;HTML 使用 HTML 注释;YAML、Shell 等采用合法的行注释。保留 shebang、编码声明、许可证等必须前置的内容及框架语法要求,将元数据放在其后的最早合法位置。
- 不向严格 JSON、图片、字体、二进制文件等不支持注释的格式插入文件头;不修改依赖、锁文件、构建产物或自动生成文件来添加元数据。无需为这些例外创建额外的元数据文件。
Vue 文件头示例(作者和时间按上述规则确定):
<script setup lang="ts">
/**
* @author Brave
* @date 2026-09-20T15:30:00+08:00
* @description 用户搜索页面,负责搜索条件、请求状态与结果展示。
*/
</script>
JS / TS / CSS / SCSS 文件头格式:
/**
* @author Brave
* @date 2026-09-20T15:30:00+08:00
* @description 用户接口模块,负责请求发送与响应适配,不处理页面提示。
*/
HTML 或无 script 的 Vue 文件头格式:
<!--
@author Brave
@date 2026-09-20T15:30:00+08:00
@description 用户信息展示模板。
-->
API、异步与状态管理
- 复用现有 HTTP 封装,先确认返回 AxiosResponse、响应体还是业务 data,再声明类型和解包;禁止凭经验重复取
.data。 - HTTP 层处理连接配置和通用错误转换,业务 API 层负责路径、参数及响应适配,调用层负责 loading、业务提示和页面流程。
- 原生 Axios GET 查询参数写入配置的 params;自定义封装严格按现有签名调用。
- 从真实接口文档、后端代码或已有类型获取契约,不虚构 URL、成功码、字段与认证方案。关键契约缺失时提出最小问题;允许先实现独立 UI,并明确待接入部分。
- 错误提示明确归属,避免全局与页面重复提示;不吞异常并返回假成功或空列表。
- 请求覆盖 loading、失败、空结果与成功状态;提交设置防重复触发状态,严格幂等由后端配合。
- 搜索和筛选请求采用取消或请求序号,防止旧响应覆盖新结果;finally 也必须确认请求仍有效后更新共享 loading。
- 页面卸载后避免写入失效状态;主动取消不作为普通失败提示。
- 不自动重试可能产生重复写入的操作,除非契约提供幂等保障。
- 响应式复用行为放 composable,纯计算放 utils;composable 默认在函数内创建状态,以普通对象返回 ref 与方法。
- 不在模块顶层意外共享 composable 状态;共享必须明确设计。
- Store 仅保存确需跨页面同步的状态,业务修改推荐 actions,派生数据使用 getters / computed。
- Pinia 状态解构使用 storeToRefs;actions 可直接解构。退出登录清理用户相关状态与缓存。
样式、路由与资源
- 组件样式默认 scoped,全局 reset 和设计变量集中管理。
- 使用语义 class;避免 ID 选择器、过深嵌套、泛化全局覆盖和随意 !important。
- 简单类使用 kebab-case,复杂组件使用一致的 BEM 命名;颜色、间距、圆角、层级复用项目变量。
- 优先通过组件库公开 API 或变量改样式;必须使用 :deep 时限定在当前组件根类并说明原因。
- 静态样式使用 class,真正动态值使用样式绑定或 CSS 变量;按产品要求适配尺寸,不擅自改动设计系统。
- 路由 name 唯一,页面默认懒加载;解析并校验 route params / query,不用断言代替转换。
- 路由守卫仅控制前端流程,真实权限依赖服务端;URL 不承载密码或令牌。
- 跨目录使用现有别名,模块内使用相对路径;新增别名时保证构建与 TS 配置一致。
- 普通模块优先具名导出,SFC 和工具配置遵循框架要求。
- 资源路径兼容部署 base;public 资源路径不带
/public前缀,不靠不可分析的任意路径拼接收集构建资源。 - 不导入依赖包未公开的内部文件。
配置与交付检查
- 格式以现有工具为准;无配置时默认两空格、单引号、分号、LF、末尾换行。仅在配置任务范围内新增格式工具,不为普通功能强制安装工具链。
- 使用现有包管理器及锁文件,不生成第二种锁文件。敏感配置不写进客户端环境变量;VITE 前缀不具备保密能力。
- 不记录密码、验证码、令牌或完整敏感响应;不将不可信 HTML 直接传给 v-html。
- 检查本次新增或修改的可注释源文件是否包含 @author、@date、@description,作者与创建时间是否真实或明确标注未知,修改时是否保留原始创建信息。
- 审查文件位置、命名、类型、注释和模块依赖是否一致,检查 props 修改、key、请求竞态及副作用清理。
- 运行适用的现有 lint、类型检查、构建和相关测试;识别 Vite 转译不等于 Vue / TS 类型检查。检查缺失或环境受限时如实说明,不声称通过。
- 为关键逻辑变更与缺陷修复选择有价值的验证,不为简单可逆展示变更机械增加测试,不为满足测试修改无关代码。
- 不自动提交、部署或生成规范说明文档;交付用户要求的实现,简述实际修改与验证结果。