当前位置: 首页 > news >正文

Vue 3 前端工程化规范

Vue 3 前端工程化规范

前言

我们遵循以下核心工程原则,这些原则是所有规范的基石:

  • 清晰胜于抖机灵 (Clarity over Cleverness):代码首先是写给人读的,其次才是给机器执行的。优先保证代码的直观和易于理解。

  • 单一职责原则 (Single Responsibility Principle):每个模块、组件或函数都应该只负责一项功能,并把它做好。

  • 自动化优于约定 (Automation over Convention):凡是能通过工具自动保证的规范,就不要依赖于人的记忆和自觉。

  • 小步提交,持续集成 (Small Commits, Continuous Integration):通过小而专注的 Git 提交,简化代码审查,并利用 CI/CD 流水线尽早发现问题。

  • 测试是项目的一部分,不是附属品 (Testing is a Feature):未经测试的代码在设计上就是有缺陷的。单元测试是保证代码质量和未来重构信心的关键。

  • 拥抱类型系统。 TypeScript 不是可选项,而是必需品。它能在编码阶段发现大量潜在错误,并极大地提升代码的可维护性。

我们围绕可维护性、可测试性和团队协作效率等核心理念,整理了这样一份Vue3前端代码规范文档。

1. 文档概述

1.1 文档目的

本文档旨在为基于 Vue 3 技术栈的前端项目提供一套统一的开发标准、最佳实践和协作流程。目标是提升代码的可读性、可维护性、健壮性团队协作效率,确保项目在整个生命周期内保持高质量和技术领先性。

1.2 适用范围

本文档适用于所有参与项目的前端开发工程师、测试工程师、架构师及项目经理。它是代码审查(Code Review)、技术决策和新成员入职培训的核心依据。

2. Vue 3 编码规范

2.1. 函数与变量
2.1.1. 优先使用箭头函数

在 Vue 3 的 <script setup> 组合式 API 场景下,函数通常不依赖 this 上下文,使用箭头函数可以保持代码风格统一。

  • 【推荐】
// 使用 const 和箭头函数定义所有本地函数
const fetchUserData = async (userId) => {// ...
};
  • 【反例】
// 风格不统一,降低可读性
function fetchUserData() { /* ... */ }
const updateUser = () => { /* ... */ };
  • 评审要点:

    • 检查是否存在 function 和箭头函数的无理由混用。

    • 方法定义是否一致。

2.1.2. 使用 **let****const**

const 优先,仅在变量需要被重新赋值时使用 let。禁止使用 var 以避免变量提升带来的潜在问题。

  • 【推荐】
const MAX_RETRIES = 3;
let currentUser = await fetchUser();
currentUser = 'guest';
  • 【反例】
var MAX_RETRIES = 3; // 禁止使用 var
  • 评审要点:

    • 代码中是否还存在 var 关键字。
2.2. 数据请求 (异步操作)
2.2.1. 使用 **async/await****try...catch**

async/await 提供了更扁平、更易读的异步代码结构。必须使用 try...catch 块来捕获和处理潜在的 API 错误。

  • 【推荐】
const getTableData = async () => {isLoading.value = true;try {const response = await getTableDataApi({ /* params */ });tableData.value = response.data;} catch (error) {console.error("Failed to fetch table data:", error);// 向用户显示错误提示showErrorToast('数据加载失败');} finally {isLoading.value = false;}
};
  • 【反例】
// 1. 回调地狱,难以阅读和维护
getTableDataApi().then(res1 => {getMoreDataApi(res1.id).then(res2 => {// ...});
});// 2. 缺少错误处理
const fetchData = async () => {const response = await someApi(); // 如果 someApi() reject,整个函数会崩溃data.value = response;
}
  • 评审要点:

    • 所有异步请求是否都被 try...catch 包裹?

    • 是否有清晰的 loading 状态管理?

    • 错误处理是否对用户友好(例如,显示提示)?

    • 是否存在超过一层的 .then() 嵌套?

2.2.2. 合理利用 **Promise.all****Promise.allSettled**

当多个 API 请求没有依赖关系时,应使用 Promise.all 并发执行以缩短总加载时间。

  • 【推荐】
// 场景:页面需要同时加载用户信息和配置信息
const loadPageData = async () => {try {const [userData, configData] = await Promise.all([fetchUserApi(),fetchConfigApi(),]);user.value = userData;config.value = configData;} catch (error) {// ...}
};
  • 评审要点:

    • 是否存在可以用 Promise.all 优化的串行 await 调用?
2.3. 响应式变量
  • 【推荐】 对于需要深度响应的大对象或数组,使用 reactive。对于基本类型或只需替换整个对象的场景,使用 ref

  • 【性能优化】 对于只有顶层属性需要响应性的大型、深层嵌套对象(例如,从后端获取的只读列表),应使用 shallowRefshallowReactive 来避免不必要的性能开销。

2.4. 逻辑分支优化

避免深层嵌套的 if-else 语句,它们会显著增加代码的“圈复杂度”,使其难以理解和测试。

  • 【推荐】使用卫语句 (Guard Clauses) 或映射表 (Map)
// 卫语句(提前返回)
function processPayment(user, order) {if (!user) {console.error("User not logged in.");return;}if (!order || order.status !== 'pending') {showErrorToast("Invalid order.");return;}// ...核心逻辑...
}// 映射表处理多状态
const statusActions = {pending: () => handlePending(),shipped: () => handleShipped(),delivered: () => handleDelivered(),default: () => handleDefault(),
};
const action = statusActions[order.status] || statusActions.default;
action();
  • 【反例】
// 深度嵌套,难以阅读
if (user) {if (order) {if (order.status === 'pending') {// ...核心逻辑...} else {showErrorToast("Invalid order status.");}} else {showErrorToast("No order found.");}
} else {console.error("User not logged in.");
}
  • 评审要点:

    • if 语句的嵌套是否超过了2层?

    • 是否存在可以用映射表或策略模式优化的长 if-else if 链?

2.5. 代码整洁度
  • 【推荐】 及时删除被注释掉的代码、调试用的 console.logdebugger 语句。版本控制系统(Git)是你的安全网,无需在代码中保留历史遗迹。

  • 【推荐】 单个文件(特别是Vue组件)的行数应尽量控制在 400行 以内。超过这个长度通常意味着组件承担了过多的职责,需要进行拆分(例如,抽取为子组件或 composable 函数)。

3. 组件与文件命名规范

3.1. 文件命名
  • Vue 组件: 大驼峰 (PascalCase),如 UserProfile.vue

  • Composable 函数: 小驼峰 (camelCase),并以 use 开头,如 useFormValidation.ts

  • 其他 TS/JS 文件: 小驼峰 (camelCase) 或大驼峰 (PascalCase) 均可,但项目内需统一。推荐 小驼峰 (如 apiClient.ts, utils.ts)。

3.2. 组件 **name** 属性

所有组件都必须显式声明一个 name 属性,且与文件名保持一致。这对于 Vue Devtools 调试和组件递归至关重要。

  • 【推荐】

JavaScript

// 在 UserProfile.vue 中
export default defineComponent({name: 'UserProfile',// ...
});
  • 评审要点:

    • 所有 .vue 文件是否都有一个匹配文件名的 name 属性?

4. Composable (Hooks) 使用规范

Hooks 是 Vue 3 中逻辑复用和功能拆分的核心。

  • 【推荐】将可复用的逻辑(如弹窗控制、数据获取、表单状态)封装成 **composable** 函数。
// composables/useModal.ts
import { ref } from 'vue';export function useModal() {const isVisible = ref(false);const openModal = () => isVisible.value = true;const closeModal = () => isVisible.value = false;return { isVisible, openModal, closeModal };
}// 在组件中使用
// const { isVisible, openModal, closeModal } = useModal();
  • 【反例】将所有状态和方法都堆砌在一个巨大的 **reactive** 对象中。
// 这种写法将 UI 状态、表单数据和控制方法耦合在一起,难以复用和测试
const editModel = reactive({isShow: false,form: { name: '...' },showFunc: () => { /* ... */ },cancelFunc: () => { /* ... */ },
});
  • 评审要点:

    • 组件中是否存在可以被抽象为 composable 的重复逻辑块?

    • composable 是否遵循单一职责原则?

5. 性能优化

  • 路由懒加载: 所有路由必须使用动态导入 (() => import(...)) 实现懒加载。

  • 异步组件: 对于非首屏、体积较大或只在特定条件下渲染的组件(如复杂的弹窗、图表),使用 defineAsyncComponent

  • 善用 **v-once** **v-memo**: 对于纯静态内容,使用 v-once。对于渲染开销大且依赖特定变量的列表,使用 v-memo

  • 虚拟滚动: 对于超过100项的长列表,应采用虚拟滚动技术(可使用 vue-virtual-scroller 等库)。

  • 减少依赖体积: 优先选用 lodash-es 等支持 tree-shaking 的库。定期使用 vite-bundle-analyzer 分析打包产物,移除不必要的依赖。

6. Git 工作流与提交规范

6.1 Git 分支模型

推荐使用简化的 Git-Flow 模型:

  • main: 主分支,用于部署生产环境,代码必须是稳定且经过测试的。只能从 develop 分支合并。

  • develop: 开发主分支,集成了所有已完成的功能。是所有功能分支的父分支。

  • feature/<feature-name>: 功能开发分支,从 develop 创建。命名应清晰,如 feature/user-authentication

  • fix/<fix-name>: Bug 修复分支,从 develop(或紧急情况下从 main)创建。

6.2 Git 提交规范

采用 Conventional Commits 标准,强制通过 commitlint 工具校验。

格式: <type>(<scope>): <subject>

  • 常用 **type**:

    • feat: 新增功能

    • fix: 修复 Bug

    • docs: 仅修改文档

    • style: 代码格式调整(不影响代码逻辑)

    • refactor: 代码重构(既不新增功能,也不修复 Bug)

    • perf: 性能优化

    • test: 新增或修改测试

    • chore: 构建流程、辅助工具的变更

  • 示例:

# 良好示例
feat(user): add user login and registration functionality
fix(form): correct validation logic for email field
refactor(api): abstract api calls into a dedicated service module

7. 项目结构

统一的目录结构是高效协作的基础。推荐以下结构:

/src
├── api/                # API 请求模块 (按业务划分)
│   ├── auth.ts
│   └── user.ts
├── assets/             # 静态资源 (图片, 字体等)
├── components/         # 全局组件
│   ├── base/           # 基础 UI 组件 (BaseButton.vue)
│   └── business/       # 全局业务组件 (UserSelector.vue)
├── composables/        # Vue Composition API 函数 (Hooks)
│   └── usePagination.ts
├── config/             # 全局配置
│   └── index.ts
├── layouts/            # 布局组件
│   └── DefaultLayout.vue
├── router/             # 路由配置
│   └── index.ts
├── stores/             # Pinia 状态管理
│   ├── user.ts
│   └── index.ts
├── styles/             # 全局样式
│   ├── main.scss
│   └── _variables.scss
├── types/              # TypeScript 类型定义
│   └── api.d.ts
├── utils/              # 工具函数
│   └── format.ts
├── views/              # 页面级组件 (路由入口)
│   ├── user/
│   │   ├── UserProfile.vue
│   │   └── UserList.vue
│   └── Home.vue
├── App.vue             # 根组件
└── main.ts             # 应用入口

8. 组件化开发规范

8.1 组件分类标准

明确组件的职责分类,有助于合理规划项目结构和提升复用性。

组件分类职责说明示例存放位置
基础组件 (Base Components)不包含任何业务逻辑,纯粹的 UI 展示和交互。具有极高的复用性,通常带有 BaseApp 前缀。BaseButton.vue, BaseInput.vue, BaseModal.vuesrc/components/base/
业务组件 (Business Components)封装特定业务逻辑,由多个基础组件组合而成。与特定业务场景强相关,可在项目内多处复用。UserCard.vue, OrderHistoryTable.vuesrc/components/business/ 或 页面私有组件 src/views/xxx/components/
页面组件 (View Components)作为路由的入口,负责组织页面布局和业务组件,处理当前页面的数据请求与状态管理。UserProfile.vue, HomePage.vuesrc/views/
布局组件 (Layout Components)定义应用的整体页面结构,如页头、侧边栏、内容区等。DefaultLayout.vue, AdminLayout.vuesrc/layouts/

8.2 组件编写标准

8.2.1 命名规范
  • 文件名: 采用大驼峰命名法 (PascalCase),如 MyComponent.vue

  • 组件 **name** 属性: 必须添加 name 选项,且与文件名保持一致。这对于 Vue Devtools 调试至关重要。

8.2.2 <script setup> 内部顺序

为了保持一致性和可读性,推荐遵循以下顺序:

<script setup lang="ts">
// 1. imports
import { ref, computed } from 'vue';
import ChildComponent from './ChildComponent.vue';// 2. 类型定义 (Props, Emits, etc.)
interface Props {title: string;items: string[];
}// 3. defineProps, defineEmits, defineExpose
const props = withDefaults(defineProps<Props>(), {title: 'Default Title',
});
const emit = defineEmits<{(e: 'select', item: string): void;
}>();
defineExpose({ reset });// 4. 响应式状态 (State)
const internalState = ref(0);// 5. 计算属性 (Computed Properties)
const formattedTitle = computed(() => `--- ${props.title} ---`);// 6. 侦听器 (Watchers)
watch(() => props.items, (newVal) => {// ...
});// 7. 生命周期钩子 (Lifecycle Hooks)
onMounted(() => {// ...
});// 8. 方法 (Methods)
function handleClick() {emit('select', 'some-item');
}function reset() {internalState.value = 0;
}
</script>
8.2.3 Props & Emits
  • 必须使用 TypeScript 进行类型定义,以获得最终的类型安全。

  • props 提供合理的默认值 (withDefaults)。

  • 事件 (emits) 必须明确定义其名称和载荷类型。

// 【推荐】清晰、类型安全
interface Props {user: User;isVisible?: boolean;
}
const props = withDefaults(defineProps<Props>(), {isVisible: false,
});const emit = defineEmits<{(e: 'update:isVisible', value: boolean): void;(e: 'submit', data: User): void;
}>();// 【反例】类型不明确,难以维护
defineProps(['user', 'isVisible']);
defineEmits(['update:isVisible', 'submit']);
8.2.4 样式规范
  • 强制使用 **<style scoped>** 避免全局样式污染。

  • 若需修改子组件样式,优先使用 CSS 变量。万不得已时,使用 :deep() 选择器,但需谨慎。

8.3 组件通信标准

根据场景选择最合适的通信方式。

通信场景推荐方式优点缺点/注意事项
父 -> 子Props单向数据流,清晰易懂,性能好。仅限直系父子。
子 -> 父Emits标准事件模式,解耦。仅限直系父子。
祖先 -> 后代Provide / Inject避免 Props 逐层传递(Prop Drilling)。数据来源不直观,可能导致耦合。
兄弟或任意组件Pinia (状态管理)集中式状态管理,数据流可预测,Devtools 支持强大。增加了少量模板代码。

【反例】禁止使用 Event Bus (如 mitt.js) 虽然 Event Bus 能实现任意组件通信,但它会导致数据流混乱,难以追踪和调试,在大型项目中是灾难性的。Pinia 是其完美的替代方案。

9. 单元测试规范

单元测试是代码质量的守护神。我们使用 Vitest 作为测试框架,Vue Test Utils 作为组件测试工具库。

9.1 测试标准

  • 公共组件、Hooks 函数、工具函数必须编写单元测试。

  • 业务组件应测试其核心交互逻辑。

  • 测试覆盖率目标:核心模块 > 90%,项目平均 > 80%。

9.2 编写规范

  • 测试文件名: *.spec.ts*.test.ts

  • 测试关注点: 测试组件的“公共契约”,即 Props、Events 和 Slots,而不是内部实现细节。

  • 遵循 AAA 模式:

    • Arrange (安排): 准备测试环境,如挂载组件、设置 props。

    • Act (行动): 执行操作,如触发点击事件、修改输入框。

    • Assert (断言): 验证结果是否符合预期,如检查 DOM 变化、emit 事件。

9.3 示例

测试一个基础按钮组件 (BaseButton.spec.ts)
// 【正例】
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import BaseButton from '../BaseButton.vue';describe('BaseButton.vue', () => {// 测试点1:渲染it('should render the slot content', () => {// Arrangeconst wrapper = mount(BaseButton, {slots: {default: 'Click Me',},});// Assertexpect(wrapper.text()).toBe('Click Me');});// 测试点2:交互 (emit)it('should emit a click event when clicked', async () => {// Arrangeconst wrapper = mount(BaseButton);// Actawait wrapper.trigger('click');// Assertexpect(wrapper.emitted()).toHaveProperty('click');expect(wrapper.emitted('click')).toHaveLength(1);});// 测试点3:属性 (props)it('should be disabled when the disabled prop is true', async () => {// Arrangeconst wrapper = mount(BaseButton, {props: {disabled: true,},});// Assertexpect(wrapper.attributes('disabled')).toBeDefined();// Actawait wrapper.trigger('click');// Assertexpect(wrapper.emitted('click')).toBeUndefined();});
});
// 【反例】测试内部实现
it('should have a specific internal class when hovered', () => {// 这是一个糟糕的测试,因为它依赖于组件的内部样式实现,// 一旦样式类名改变,测试就会失败,即使组件功能完好。
});

10. 代码审查 (Code Review) 标准

Code Review (CR) 是保证代码质量、促进知识共享的关键环节。

10.1 CR 要点 (Checklist)

审查者应重点关注以下方面:

  1. 功能性 (Functionality): 代码是否正确地实现了需求?是否考虑了边界情况?

  2. 可读性 (Readability): 变量和函数命名是否清晰?逻辑是否易于理解?是否存在过于复杂的代码?

  3. 规范性 (Consistency): 是否遵循了本文档中定义的所有规范?

  4. 可维护性 (Maintainability): 代码是否易于修改和扩展?是否存在硬编码或“魔法数字”?

  5. 性能 (Performance): 是否存在明显的性能问题?(如不必要的 watch deep、过大的 VNode 渲染)

  6. 测试 (Testing): 是否包含了必要的单元测试?测试用例是否有效且覆盖了关键逻辑?

  7. 注释 (Comments): 复杂的逻辑或“为什么”这么做的原因是否添加了注释?

11. 自动化与工程化

11.1 工具链

  • 包管理器: pnpm (推荐,速度快且节省磁盘空间)

  • 构建工具: Vite

  • 代码规范: ESLint (集成 vue, typescript 插件)

  • 代码格式化: Prettier (与 ESLint 集成,解决格式冲突)

  • Git 钩子: Husky + lint-staged

  • Commit 规范: @commitlint/cli

Vue 3 前端工程化规范

11.2 自动化流程

11.2.1 本地开发 (Pre-commit)

通过 huskylint-staged 配置 pre-commit 钩子,在代码提交前自动执行:

  1. 格式化: prettier --write 对暂存区的文件进行格式化。

  2. 代码检查: eslint --fix 对暂存区的文件进行语法和规范检查。

  3. (可选)单元测试: vitest related --run 仅运行与修改文件相关的测试。

**package.json** 配置示例:

{"scripts": {"prepare": "husky install"},"lint-staged": {"*.{js,ts,vue}": ["eslint --fix", "prettier --write"],"*.{scss,css}": ["stylelint --fix"]}
}
11.2.2 持续集成 (CI/CD)

GitHub Actions 为例,配置工作流,在提交 Pull Request 时自动触发。

**.github/workflows/ci.yml**:

name: Frontend CIon:push:branches: [ main, develop ]pull_request:branches: [ main, develop ]jobs:build-and-test:runs-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v3- name: Setup pnpmuses: pnpm/action-setup@v2with:version: 8- name: Setup Node.jsuses: actions/setup-node@v3with:node-version: '18'cache: 'pnpm'- name: Install dependenciesrun: pnpm install --frozen-lockfile- name: Lint and Format Checkrun: pnpm lint- name: Run Unit Testsrun: pnpm test:run --coverage # 运行测试并生成覆盖率报告- name: Build Projectrun: pnpm build# 可选:上传覆盖率报告到 Codecov 等服务# - name: Upload coverage reports to Codecov#   uses: codecov/codecov-action@v3

这个 CI 流程确保了每一份合入 developmain 分支的代码都经过了严格的自动化校验,极大地保障了主干分支的健康度。


文章转载自:

http://uxbWczSa.cprmp.cn
http://w4NdoPx1.cprmp.cn
http://TgBfango.cprmp.cn
http://5U4sF3se.cprmp.cn
http://8r6USkfI.cprmp.cn
http://RZ1mDDXZ.cprmp.cn
http://lMunY5DV.cprmp.cn
http://DNnonzI5.cprmp.cn
http://9BApT2fW.cprmp.cn
http://3kxT76zv.cprmp.cn
http://LhgYG6lK.cprmp.cn
http://gz1e0SzC.cprmp.cn
http://OlGLVmk7.cprmp.cn
http://m1VjZwU3.cprmp.cn
http://SN3auBG4.cprmp.cn
http://fY2sSYoc.cprmp.cn
http://BhYaxSck.cprmp.cn
http://FrW6P2v3.cprmp.cn
http://SpMJs7qM.cprmp.cn
http://A2wrWplJ.cprmp.cn
http://5yLl3D1H.cprmp.cn
http://P9hqbxJn.cprmp.cn
http://Pb6Q8kR6.cprmp.cn
http://peHIXVEI.cprmp.cn
http://G6sd6jZp.cprmp.cn
http://zeikM4p4.cprmp.cn
http://l8Gt4u5G.cprmp.cn
http://lHALFcfL.cprmp.cn
http://UTLI0NfE.cprmp.cn
http://x3y4bERJ.cprmp.cn
http://www.dtcms.com/a/384726.html

相关文章:

  • NLP Subword 之 WordPiece 算法原理
  • 【SQL】MySQL中空值处理COALESCE函数
  • Kafka实时数据管道:ETL在流式处理中的应用
  • VBA数据结构深度解析:字典对象与集合对象的性能终极对决
  • 查看当前虚拟环境中安装的 PyTorch 版本
  • 布尔运算-区间dp
  • WWW‘25一通读 |图Anomaly/OOD检测相关文章(1)
  • 视频分类 pytorchvideo
  • RabbitMQ 基础概念与原理
  • 专题:2025中国消费市场趋势与数字化转型研究报告|附360+份报告PDF、数据仪表盘汇总下载
  • 预制菜行业新风向:企业运营与商家协同发展的实践启示
  • 晶台光耦 KL6N137 :以精密光电技术驱动智能开关性能提升
  • 贪心算法应用:最短作业优先(SJF)调度问题详解
  • javaee初阶 文件IO
  • 如何调整滚珠丝杆的反向间隙?
  • Python项目中的包添加后为什么要进行可编辑安装?
  • daily notes[45]
  • 基于51单片机的蓝牙体温计app设计
  • Git版本控制完全指南
  • 【CSS】一个自适应大小的父元素,如何让子元素的宽高比一直是2:1
  • 前端通过地址生成自定义二维码实战(带源码)
  • Android Doze低电耗休眠模式 与 WorkManager
  • 用 Go 重写 adbkit:原理、架构与实现实践
  • 通过Magisk service.d 脚本实现手机开机自动开启无线 ADB
  • NineData社区版 V4.5.0 正式发布!运维中心新增细粒度任务权限管理,新增MySQL至Greenplum全链路复制对比
  • centos配置环境变量jdk
  • 基于“能量逆流泵“架构的220V AC至20V DC 300W高效电源设计
  • 归一化实现原理
  • 云原生安全如何构建
  • 条件生成对抗网络(cGAN)详解与实现