前言
现状背景
随着前端工程复杂度的不断提升,多技术栈并存、多人协作开发已成为常态。但在实际开发中,常因代码风格不统一、目录结构随意、组件设计缺乏约束等问题,导致代码可读性差、维护成本高、协作效率低下,甚至引发难以排查的隐性 Bug。技术债务的积累严重制约了项目的迭代效率与团队交付质量。
规范必要性
在快速迭代的业务场景下,统一的开发规范是保障项目质量的核心基础设施。它能有效解决以下问题:
- 降低认知成本:消除团队成员对代码风格的争议,让新人快速融入
- 提升协作效率:通过约束文件/目录/命名规则,避免"碎片化编码"现象
- 增强可维护性:规范化的组件设计和代码逻辑,减少"牵一发而动全身"的风险
- 控制技术债务:通过 Git 流程约束和代码审查机制,防止劣质代码进入主干
JS
格式规范
基于prettierrc规范进行配置,以VScode为例,安装prettierrc插件进行代码自动格式化,统一代码书写规范。
示例配置:
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false, // 语句末尾加分号
"tabWidth": 4, // 缩进空格数
"singleQuote": true, // 使用单引号
"printWidth": 200, // 单行最大长度
"trailingComma": "none", // 多行时始终添加尾随逗号
"bracketSameLine": true, //闭合标签与属性同行
}
变量/函数
- 变量名书写规范
// 变量命名(camelCase)
// 声明变量优先以const声明
const firstName = "xxx" ✅
let stuNum = "xxx"; ✅
// 常量命名(全大写,单词间用下划线分隔)
const API_KEY = "xxx"; ✅
// 私有变量(以_开头)
const _privateVar = 'xxx' ✅
// 布尔变量通常以 is, has, can 等开头
let isShow = fasle ✅
let hasPermission = false ✅
let canEdit = true ✅
// 尽量避免语义不清晰的缩写
let userCount = 10 ✅
let usrCnt = 10 ❌
- 函数名书写规范
// 类/构造函数
class UserProfile { ✅
constructor(name) {
this._privateName = name;
}
}
// 函数
function calculateTotalPrice(items) { ✅
// ...
}
- 函数单一功能原则(SRP)
✅
// 1. 验证用户数据
function validateUser(user) {
if (!user.name || !user.email) {
throw new Error("无效的用户数据");
}
return true;
}
// 2. 格式化用户数据
function formatUserData(user) {
return {
...user,
name: user.name.trim(),
email: user.email.toLowerCase(),
id: generateUserId()
};
}
// 3. 渲染用户到DOM
function renderUserToDOM(user) {
const userList = document.getElementById('user-list');
const listItem = document.createElement('li');
listItem.textContent = `${user.name} - ${user.email}`;
userList.appendChild(listItem);
}
// 4. 保存用户数据
function saveUserToStorage(user) {
localStorage.setItem('currentUser', JSON.stringify(user));
}
// 主函数协调各个单一职责的函数
function processUserData(user) {
try {
validateUser(user);
const formattedUser = formatUserData(user);
renderUserToDOM(formattedUser);
saveUserToStorage(formattedUser);
} catch (error) {
console.error(error.message);
// 可以添加错误处理逻辑
}
}
❌
//一个函数只做一件事
// 这个函数做了太多事情:验证、数据处理、DOM操作
function processUserData(user) {
// 1. 验证
if (!user.name || !user.email) {
console.error("无效的用户数据");
return;
}
// 2. 数据处理
user.name = user.name.trim();
user.email = user.email.toLowerCase();
user.id = generateUserId();
// 3. DOM操作
const userList = document.getElementById('user-list');
const listItem = document.createElement('li');
listItem.textContent = `${user.name} - ${user.email}`;
userList.appendChild(listItem);
// 4. 保存到本地存储
localStorage.setItem('currentUser', JSON.stringify(user));
}
- 逻辑判断
//建议维护一个逻辑判断常量
const TYPE_MODE = {
ADD: 1, //新增
DELETE: 2, //删除
UPDATE: 3 //更新
}
//使用else if ✅
if(type === TYPE_MODE.ADD) {
//...
}else if(type === TYPE_MODE.DELETE) {
//...
}else if(type === TYPE_MODE.UPDATE) {
//...
}
或 ✅
if(type === TYPE_MODE.ADD) { return; }
if(type === TYPE_MODE.DELETE) { return; }
if(type === TYPE_MODE.UPDATE) { return; }
//并列使用多个if ❌
if(type === TYPE_MODE.ADD) {}
if(type === TYPE_MODE.DELETE) {}
if(type === TYPE_MODE.UPDATE) {}
文件引入
import 文件使用绝对路径,不使用相对路径,便于代码复用及目录修改。
import { func } from @/utils/index.js ✅
import { func } from ../../utils/index.js ❌
CSS
命名规范(BEM 推荐)
/* Block__Element--Modifier */
.user-card { /* 块 */ }
.user-card__header { /* 元素 */ }
.user-card--active { /* 修饰符 */ }
/* 禁止: */
.userCardHeader { /* 驼峰 ❌ */ }
.user-card-header { /* 全连字符 ❌ */ }
样式代码
充分利用样式继承,减少代码冗余。
.course-card {
font-size: 14px;
color: #000000;
font-weight: 500;
.title {
font-size: 14px; ❌
color: #000000; ❌
font-weight: 400;
}
}
禁止❌
组件中样式不使用scoped,导致全局样式污染
<style> ❌
.el-dialog { height: 100px; }
</style>
滥用 !important
.error { color: red!important; } // 仅用于覆盖第三方样式 ❌
命名语义不清晰
.class-1: { color: #FFFFFF; } ❌
HTML
逻辑判断
复杂的逻辑判断建议使用计算属性
<template>
<span v-if="isTransform" class="w-unit">w</span>
</template>
<script setup>
const isTransform = computed(() => {///是否需要转换w
return props.needTransform && props.endVal >= 100000
})
</script>
组件标签
引入组件以PascalCase方式书写和使用。
<template>
<ToolTipPro :content="name" :showArrow="true" />
</template>
<script setup>
import ToolTipPro from '@/components/ToolTipPro.vue';
</script>
禁止❌
避免行内样式过长
<div style="width: 100px; height: 50px; margin: 10px..."> ❌
拒绝无意义嵌套,尽量少的嵌套来还原设计稿
<div><span>内容</span></div> ❌
路由规范
命名规范
- 路径命名使用 kebab-case 命名法(如
/user-center
) - 路由命名使用 PascalCase 命名法(如
UserCenter
) - 路由名与组件名保持一致
- 避免使用通用名称如
Home
、Index
,应使用业务相关名称
// 采用标准定义格式
const routes = [
{
path: '/user-center',
name: 'UserCenter', // 大驼峰命名
component: () => import('@/views/user/UserCenter.vue'), // 懒加载
meta: {
title: '用户中心',
},
children: [...] // 嵌套路由
}
]
路由参数
路径参(param)和查询参数(query)都使用 camelCase 命名法
// 路由跳转时传递
this.$router.push({
name: 'HomeworkDetail',
params: {
homeworkId: item.homeworkId
},
query: {
aiCourseId: route.query.courseId,
aiClassId: route.query.classId,
}
})
路由跳转
编程式导航优先
// 使用命名路由(最佳实践)✅
this.$router.push({
name: 'UserDetail',
params: { userId: 10012 },
query: { from: 'home' }
})
// 避免硬编码路径
this.$router.push(`/user-detail/${userId}?from=home`) ❌
目录文件
基础结构
命名规范
4个基本原则
- index.js 或者 index.vue,统一使用小写字母开头的(kebab-case)命名规范
/router/index.js ✅
/components/user-card/index.vue ✅
- hook文件,使用小写字母开头的(camelCase)命名规范
/hooks/useCustomMade.js ✅
- 组件或类文件,统一使用大写字母开头的(PascalCase)命名规范
/layouts/DefaultLayout.vue ✅
/model/BasicNode.js ✅
- 其它非组件或非类文件(包括文件夹),统一使用小写字母开头的(kebab-case)命名规范
/utils/format-date.js ✅
/pages/user-center ✅
components目录
- 全局组件目录在src/下创建,仅此1个,用于存放全局组件。
- 局部组件放在pages/xxx/components/,避免全局污染。
- 基础组件加前缀Base,eg:BaseButton
Git
分支名称
分支名称以(kebab-case)命名规范,要求单词拼写完整,名称含义清晰明了,禁止使用中文,英文单词尽量不要超过3个。
分支类型
按使用场景分为6个类型:
- 主分支:master
- 发版分支:pre-master
- 预发环境分支:release-branch
- bug修复分支:fix-xxx
- 迭代主分支:feature-xx
- 功能模块开发分支:dev-xx
主分支、发版分支、预发分支为固定名称,不可变更。 bug修复分支、迭代主分支、功能模块开发分支按业务实际迭代情况进行命名。
commit信息
commit信息范式为:前缀 + : + 内容,例如 "fix:课程列表显示重复问题"。
'feat', // 新增功能
'fix', // 修复缺陷
'style', // 代码格式(不影响功能,例如空格、分号等格式修正)
'refactor',// 代码重构(不包括 bug 修复、功能新增)
'perf', // 性能优化
'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
'chore' // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
'revert', // 回滚 commit
'docs', // 文档变更
总结
前端规范通过统一代码标准与协作流程,降低维护成本与技术债务,构建可维护、可持续的高质量工程体系。