Vue3 开发规范(审查维度)

前言

团队协作中,统一的开发规范能显著降低沟通成本和维护难度。
本文按 代码审查维度 整理 Vue3 开发规范,适用于 Vue3 + Composition API + TypeScript 技术栈,其他变体可按需调整。

审查维度概览

维度 权重 核心要求
TypeScript 规范 15 类型完整、禁止 any、interface / type 分工明确
Vue3 语法规范 15 Composition API、组件命名、Props / 事件声明
命名与项目结构 15 目录划分、文件命名、路由与导入路径
组件设计 20 Props 类型、粒度控制、通信方式
代码质量 15 格式统一、注释、错误处理、禁用的写法
性能优化 20 懒加载、响应式精准化、列表渲染、computed 规范

TypeScript 规范

  • 所有变量、函数参数、返回值必须声明类型,禁止使用 any
  • 优先使用 interface 定义对象结构,type 用于联合类型或工具类型。
  • 枚举使用 const enum 或字符串字面量联合类型。
1
2
3
4
5
6
7
8
9
10
11
// 推荐
interface UserInfo {
id: string
name: string
age: number
}

type Status = 'pending' | 'success' | 'error'

// 不推荐
const data: any = {}

Vue3 语法规范

  • 使用 <script setup lang="ts"> 语法糖,不使用 Options API
  • 组件名使用 PascalCase,模板中使用 kebab-case
  • Props 必须声明类型,使用 TypeScript 类型声明而非运行时声明。
  • 事件命名使用 camelCase,如 update:modelValue
  • 稍复杂的组件应做到模板、脚本和样式分离。

单文件组件内部按以下顺序组织:<script><template><style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script setup lang="ts">
// 1. props / emits
const props = defineProps<{ title: string }>()
const emit = defineEmits<{ close: [] }>()

// 2. 响应式状态
const visible = ref(false)

// 3. 计算属性
const displayTitle = computed(() => props.title || '默认标题')

// 4. 方法
function handleClose() {
visible.value = false
emit('close')
}
</script>

<template>
<div v-if="visible" class="modal">
<h2>{{ displayTitle }}</h2>
<button @click="handleClose">关闭</button>
</div>
</template>

命名与项目结构

命名方式速查

命名方式 规则 示例 常见用途
PascalCase 每个单词首字母大写 UserProfileMyComponent Vue 组件名、TS 类 / 接口 / 类型
camelCase 首单词小写,后续单词首字母大写 userNamegetList 变量、函数、Props、对象属性
kebab-case 全小写,单词间用 - 连接 user-profileget-list 文件名、CSS 类名、URL 路径
UPPER_SNAKE_CASE 全大写,单词间用 _ 连接 MAX_COUNTAPI_BASE_URL 常量、环境变量

推荐目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
src/
├── api/ # 接口请求
├── assets/ # 静态资源
├── components/ # 公共组件
│ └── ui/ # 通用 UI 组件
├── composables/ # 组合式函数
├── constants/ # 常量
├── layouts/ # 布局组件
├── pages/ # 页面组件
├── router/ # 路由配置
├── stores/ # 状态管理
├── styles/ # 全局样式
├── types/ # 类型定义
└── utils/ # 工具函数

文件与文件夹命名

类型 命名方式 示例
普通文件夹 kebab-case user-profile/composables/
组件文件夹 PascalCase UserProfile/BaseButton/
页面组件 kebab-case,与路由路径一致 user-profile/index.vue
通用组件 PascalCase UserProfile.vueBaseButton.vue
组合式函数 camelCase,use 前缀 useAuth.ts
工具函数 camelCase formatDate.ts
类型定义 .d.tstypes.ts 结尾 user.d.tsapi.types.ts
CSS 类名 kebab-case user-profile-card
常量 UPPER_SNAKE_CASE MAX_RETRY_COUNT = 3
1
2
3
4
src/pages/user-profile/index.vue
src/pages/user-profile/UserManager/UserManager.vue
src/composables/useAuth.ts
src/api/user.ts

路由与导入

  • 路由 path 使用小写 kebab-case,如 /user-profile/order-detail
  • 动态参数使用 camelCase,如 /user/:userId
  • 使用 @/ 别名指向 src/,避免多层相对路径。
  • 导入顺序:第三方库 → @/ 别名 → 相对路径。
1
2
3
4
5
6
7
8
9
10
11
// vite.config.ts
import path from 'node:path'
import { defineConfig } from 'vite'

export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
})
1
2
3
4
5
6
7
8
// 推荐
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import UserProfile from '@/components/UserProfile.vue'
import { formatDate } from '@/utils/formatDate'

// 不推荐
import UserProfile from '../../../components/UserProfile.vue'

组件设计

Props 定义

优先使用 TypeScript 类型声明,必须声明类型,禁止 any

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 推荐
const props = defineProps<{
id: string
title: string
count?: number
}>()

// 带默认值
const props = withDefaults(
defineProps<{
title: string
count?: number
}>(),
{ count: 0 },
)

组件粒度

  • 单一组件代码不超过 300 行,超出时应拆分子组件。
  • 通用 UI 元素(按钮、弹窗、表格)抽到 components/ui/ 目录。
  • 业务组件按功能模块分目录存放。

组件通信

  • 父子通信用 props + emit不要直接修改 props
  • 跨层级通信用 provide / inject 或状态管理库。
  • 兄弟组件优先通过共同父组件中转,避免事件总线。

代码质量

工程工具

  • 包管理使用 pnpm
  • 推荐使用 @antfu/eslint-config 或官方 eslint-plugin-vue 的 flat config 模式,配合 Prettier / oxc 统一格式化。
1
pnpm add -D eslint @antfu/eslint-config
1
2
3
4
5
6
7
// eslint.config.js
import antfu from '@antfu/eslint-config'

export default antfu({
vue: true,
typescript: true,
})
1
2
3
4
5
6
7
8
# .editorconfig
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

代码格式

  • 2 空格缩进,不使用 Tab。

  • 字符串统一使用单引号 ',模板字符串使用反引号。

  • 语句末尾不加分号(遵循项目 ESLint 配置)。

注释规范

  • 复杂逻辑必须添加注释,说明意图而非描述代码。
  • 使用 JSDoc 格式为公共函数添加文档注释。
  • TODO 注释格式:// TODO(作者): 说明
1
2
3
4
5
6
7
8
9
10
11
/**
* 计算用户折扣价格
* @param price - 原价
* @param discount - 折扣比例 (0-1)
*/
function calcDiscount(price: number, discount: number): number {
const validDiscount = Math.max(0, Math.min(1, discount))
return price * validDiscount
}

// TODO(zhangsan): 待对接支付接口

错误处理

  • 异步操作必须使用 try/catch.catch() 处理错误。
  • 不要吞掉错误,至少打印日志或上报监控。
  • 组件级错误使用 onErrorCaptured 捕获。

禁止与避免的写法

禁止 替代 / 说明
var 统一使用 const / let
== 统一使用 ===
computed 中产生副作用 仅用于派生数据,见下方说明
直接操作 DOM 优先使用 Vue 指令和 ref
多层嵌套 超过 3 层时拆分函数;需要嵌套时用 Promise 封装
工具类方法名重复 不同工具类中方法名保持唯一

代码习惯

  • 禁止多层代码嵌套,需要嵌套的使用 Promise 进行封装。
  • 工具类方法保证不同的工具类中方法名不要重复。

性能优化

组件懒加载

路由级别使用动态导入,减少首屏加载体积。

1
2
3
4
5
6
const routes = [
{
path: '/dashboard',
component: () => import('@/pages/dashboard/index.vue'),
},
]

响应式精准化

  • 小范围响应式数据用 ref,复杂对象用 reactive
  • reactive 对象解构时使用 toRefs,避免丢失响应性。
1
2
const state = reactive({ name: '', age: 0 })
const { name } = toRefs(state) // 保持响应性

列表渲染

  • v-for 必须提供稳定且唯一的 key不要使用 index
  • 大数据列表考虑虚拟滚动(如 vue-virtual-scroller)。
1
2
3
4
5
6
7
<template>
<ul>
<li v-for="item in list" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>

计算属性与缓存

派生数据统一使用 computed,不要在模板中写复杂表达式。

1
2
3
4
// 推荐
const sortedList = computed(() =>
[...list.value].sort((a, b) => a.name.localeCompare(b.name)),
)

computed禁止产生副作用——除返回值外不得对系统产生其他影响,包括:

  • 修改其他响应式变量
  • 调用 API / 发送网络请求
  • 操作 DOM 或使用定时器
  • 调用会改变外部状态的函数

图片与资源

  • 静态图片放 src/assets/ 并通过模块导入,构建时自动处理。
  • 外部大图使用懒加载(loading="lazy" 或 Intersection Observer)。
  • 图标优先使用 SVG sprite 或图标库;图片尽量使用 WebP。
1
2
3
4
5
6
7
<script setup lang="ts">
import logo from '@/assets/logo.png'
</script>

<template>
<img :src="logo" alt="logo" loading="lazy" />
</template>

协作流程

代码提交

每天进行当日代码提交。

测试发版

已改 BUG 标记后当日发版并通知测试人员。


对其他端要求

原型 / 效果图

  • 原型上面的模块和下面的页面分类名称定义清晰。
  • 效果图不要在原图上添加色块,分类要清晰。
  • 设计参考组件库,注意组件分组。
  • 设计图页面分组和排序保持一致。

接口

  • 文档返回值类型和必填要符合实际情况。
  • 按应用业务模块归类,不要产生业务不需要的接口。
  • 字段不能缺失,也不能冗余。
  • 接口变更后文档要同步更新。

测试

  • 要有版本机制,杜绝只有主干的模式。