【Vue3】【笔记】----第十一章:Vue3 开发规范手册(工程化与团队协作指南)
·
第十一章:Vue3 开发规范手册(工程化与团队协作指南)
三、编码规范(Vue 单文件组件与 JavaScript/TypeScript)
2. JavaScript/TypeScript 规范
(2)异步逻辑规范
- 优先用 async/await:禁止用
Promise链式调用(then/catch),代码更易读且便于调试:// 正确(async/await + try/catch) const fetchUser = async () => { try { const res = await getUserInfo(); // 调用异步接口 return res.data; } catch (err) { console.error('获取用户信息失败:', err); ElMessage.error('加载失败,请重试'); throw err; // 如需上层处理,可重新抛出错误 } }; // 错误(Promise 链式调用,嵌套层级深时难维护) const fetchUser = () => { return getUserInfo() .then(res => res.data) .catch(err => { console.error('获取用户信息失败:', err); ElMessage.error('加载失败,请重试'); throw err; }); }; - 禁止无处理的异步错误:所有
async/await必须包裹try/catch,或在调用时用.catch()捕获错误,避免控制台报错和逻辑中断; - 异步请求防抖/节流:高频触发的异步请求(如搜索输入框)必须加防抖(
debounce),避免无效请求,推荐用组合式函数useDebounce封装:// composables/useDebounce.js import { ref, watch, unref } from 'vue'; export function useDebounce(value, delay = 300) { const debouncedValue = ref(unref(value)); let timer; watch(value, (newVal) => { clearTimeout(timer); timer = setTimeout(() => { debouncedValue.value = newVal; }, delay); }); return debouncedValue; } // 组件中使用 <script setup> import { ref, watch } from 'vue'; import { useDebounce } from '@/composables/useDebounce'; import { searchGoods } from '@/api/goods/list'; const searchInput = ref(''); const debouncedInput = useDebounce(searchInput, 500); // 500ms 防抖 // 监听防抖后的值,触发搜索 watch(debouncedInput, async (val) => { if (val) await searchGoods({ keyword: val }); }); </script>
(3)TypeScript 类型规范
- 禁止滥用
any:除非明确兼容所有类型(如第三方库无类型声明),否则必须用具体类型、unknown或泛型;若用any,需加注释说明原因:// 正确(用 interface 定义具体类型) interface Goods { id: number; name: string; price: number; stock: number; } const goods: Goods = { id: 1, name: '手机', price: 5999, stock: 100 }; // 正确(用 unknown 替代 any,需类型断言) const handleUnknownData = (data: unknown) => { if (typeof data === 'object' && data !== null && 'id' in data) { const user = data as { id: number; name: string }; console.log(user.name); } }; // 错误(无理由滥用 any) const goods: any = { id: 1, name: '手机' }; - 接口(
interface)与类型别名(type)区分:- 定义对象/类的结构用
interface(支持扩展、实现); - 定义联合类型、交叉类型或简单类型别名用
type:// 正确(interface 定义对象结构) interface User { id: number; name: string; } interface AdminUser extends User { // 扩展接口 role: 'admin'; } // 正确(type 定义联合类型) type GoodsCategory = 'electronics' | 'clothes' | 'food'; type GoodsWithDiscount = Goods & { discount: number }; // 交叉类型
- 定义对象/类的结构用
- 泛型约束:使用泛型时,需通过
extends添加约束,避免无意义的类型范围:// 正确(泛型约束为“有 id 属性的对象”) function getById<T extends { id: number }>(list: T[], id: number): T | undefined { return list.find(item => item.id === id); } // 错误(泛型无约束,无法安全访问 id 属性) function getById<T>(list: T[], id: number): T | undefined { return list.find(item => item.id === id); // 报错:T 上不存在 id }
四、组件设计规范(高复用、低耦合)
1. 组件拆分原则
- 单一职责:一个组件只做一件事,避免“万能组件”(如
GoodsList只负责渲染商品列表,筛选逻辑拆到GoodsFilter组件); - 粒度适中:
- 公共组件:粒度细(如
Button、Input),确保高复用; - 业务组件:粒度按业务场景划分(如
OrderItem包含订单信息、操作按钮,无需拆分为更小组件);
- 公共组件:粒度细(如
- 无状态组件优先:若组件仅接收
props并渲染,无内部状态(如GoodsCard),优先设计为“纯展示组件”,减少状态管理成本。
2. Props 设计规范
- 显式类型声明:必须指定
type,可选时添加default,必选时添加required: true,禁止仅写props: ['goods']:// 正确(完整 Props 配置) const props = defineProps({ goods: { type: Object as PropType<Goods>, // TypeScript 需指定具体类型 required: true, validator: (value) => { // 自定义校验(确保有 id 和 name 属性) return 'id' in value && 'name' in value; } }, showPrice: { type: Boolean, default: true // 可选属性必须有默认值 }, maxDescLength: { type: Number, default: 50, validator: (value) => value >= 0 // 校验值为非负 } }); // 错误(无类型、无默认值、无校验) const props = defineProps(['goods', 'showPrice', 'maxDescLength']); - Props 单向数据流:子组件禁止修改
props,需通过emit通知父组件修改,避免数据流混乱:<!-- 正确(子组件通过 emit 通知父组件) --> <script setup> const props = defineProps({ count: Number }); const emit = defineEmits(['update:count']); const increment = () => { emit('update:count', props.count + 1); // 通知父组件更新 }; </script> <template> <button @click="increment">{{ props.count }}</button> </template> <!-- 父组件使用 --> <template> <Counter :count="count" @update:count="newCount => count = newCount" /> </template> <!-- 错误(子组件直接修改 props) --> <script setup> const props = defineProps({ count: Number }); const increment = () => { props.count++; // 报错:props 是只读的 }; </script> - 避免过多 Props:若组件 Props 超过 8 个,需拆分组件或合并为对象类型(如将
name、age、avatar合并为user对象)。
3. 组件通信规范
- 父子通信:优先用
props+emit,禁止用$parent/$children(耦合度高,难以维护); - 跨层级通信:
- 静态配置(如主题、权限)用
provide/inject; - 动态数据(如用户信息)用 Pinia,禁止用
provide/inject传递频繁更新的数据;
- 静态配置(如主题、权限)用
- 兄弟通信:禁止用“事件总线(mitt)”处理复杂逻辑,优先用“父组件中转”或 Pinia,避免事件流混乱。
五、状态管理规范(Pinia 最佳实践)
1. Store 设计原则
- 按业务模块拆分:一个 Store 对应一个业务领域(如
userStore处理用户相关,cartStore处理购物车相关),禁止一个 Store 包含所有状态; - 状态分类:Store 内状态按“核心状态(如
token、userId)”和“衍生状态(如isLogin= !!token)”拆分,衍生状态优先用 Getter 计算,禁止冗余存储:// 正确(衍生状态用 Getter 计算) export const useUserStore = defineStore('user', { state: () => ({ token: '', userId: '' }), getters: { isLogin: (state) => !!state.token // 衍生状态,不存储在 state 中 } }); // 错误(冗余存储衍生状态) export const useUserStore = defineStore('user', { state: () => ({ token: '', userId: '', isLogin: false // 冗余,应通过 token 计算 }) }); - Action 职责:
- 同步 Action:仅用于修改状态(如
updateUserInfo),禁止包含业务逻辑; - 异步 Action:处理异步逻辑(如接口请求),请求成功后调用同步 Action 修改状态,禁止在组件中直接调用接口后修改 Store 状态。
- 同步 Action:仅用于修改状态(如
2. 状态访问规范
- 组件中禁止直接修改 Store 状态:必须通过 Action 修改,确保状态变更可追踪(便于调试和日志记录):
// 正确(通过 Action 修改状态) const userStore = useUserStore(); userStore.loginSuccess({ token: 'xxx', userId: 1 }); // 错误(直接修改 Store 状态) const userStore = useUserStore(); userStore.token = 'xxx'; userStore.userId = 1; - 避免过度依赖 Store:组件私有状态(如表单输入值、弹窗显示隐藏)无需放入 Store,仅将“跨组件共享的状态”存入 Store。
六、工程化与协作规范
1. 代码提交规范(Git Commit 规范)
采用 Conventional Commits 规范,确保提交信息清晰,便于生成 CHANGELOG 和版本管理,格式为:<type>[optional scope]: <description>
-
type 类型:
feat:新增功能(如“feat: 新增购物车结算功能”);fix:修复 bug(如“fix: 修复购物车数量计算错误”);docs:仅修改文档(如“docs: 更新 API 文档”);style:不影响代码逻辑的格式修改(如缩进、空格);refactor:重构代码(既不新增功能也不修复 bug,如“refactor: 优化用户信息请求逻辑”);test:新增/修改测试用例;chore:构建流程、依赖管理的修改(如“chore: 更新依赖版本”)。
-
示例:
feat(cart): 新增购物车商品批量删除功能 fix(goods): 修复商品详情页图片加载失败问题 refactor(user): 重构用户登录状态管理逻辑
2. 代码审查(Code Review)规范
- 审查重点:
- 功能正确性:是否符合需求文档;
- 代码规范:是否遵循本手册规范;
- 性能问题:是否存在冗余渲染、无效请求;
- 安全性:是否存在 XSS、接口未授权访问等问题;
- 反馈原则:
- 对事不对人,反馈需具体(如“这里未加防抖,会导致频繁请求”而非“这里写得不好”);
- 复杂问题需附优化示例代码。
3. 环境配置规范
- 环境变量:通过
.env.development(开发环境)、.env.production(生产环境)、.env.test(测试环境)配置环境变量,变量名必须以VITE_开头(Vite 要求):# .env.development VITE_API_BASE_URL=http://dev.example.com/api VITE_ENV=development # .env.production VITE_API_BASE_URL=https://prod.example.com/api VITE_ENV=production - 禁止硬编码环境相关配置:如接口地址、APP 名称等,必须通过环境变量获取(
import.meta.env.VITE_API_BASE_URL)。
七、性能与安全规范
1. 性能优化规范
- 避免不必要的渲染:
- 组件 props 为对象/数组时,避免每次渲染创建新对象(如
:props="{ id: 1 }"改为const props = ref({ id: 1 }),再:props="props"); - 用
v-memo缓存列表渲染结果(如v-memo="[item.id]",仅当item.id变化时重新渲染);
- 组件 props 为对象/数组时,避免每次渲染创建新对象(如
- 接口请求优化:
- 合并重复请求(如同一时间多次调用
getUserInfo,仅发送一次请求); - 缓存请求结果(如商品详情页数据,短时间内重复访问无需重新请求)。
- 合并重复请求(如同一时间多次调用
2. 安全规范
- XSS 防护:
- 禁止直接插入 HTML(如
v-html),若必须使用,需先过滤危险标签/属性(如用DOMPurify库); - 接口返回的用户输入内容(如评论、昵称)必须做转义处理;
- 禁止直接插入 HTML(如
- 接口安全:
- 所有接口必须通过 HTTPS 访问;
- 敏感接口(如登录、支付)需加签名验证,禁止明文传输密码;
- 权限控制:
- 前端权限仅做辅助控制,核心权限逻辑必须在后端实现(如用户无权访问某接口,后端需返回 403);
- 路由守卫中必须验证用户权限,禁止通过 URL 直接访问无权限页面。
更多推荐
所有评论(0)