7.6 KiB
7.6 KiB
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 结构
默认保持以下顶层顺序:
<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。