好的编码规范不是为了限制创造力,而是让六个月后的自己和同事都能快速读懂代码。本文整理前端日常开发中最容易踩坑的规范,配合 NO / YES 对比示例。
变量
不要定义用不到的变量
// ❌ 定义了却从不使用
let kpi = 4;
function calc(a, b) {
const c = a + b;
const d = c + 1;
const e = d + a;
return e;
}
// ✅ 只保留必要变量
function calc(a, b) {
return 2 * a + b + 1;
}
命名要有语义,避免神秘缩写
// ❌ 缩写让人猜含义
let fName = 'jackie';
let lName = 'willen';
// ✅ 见名知意
let firstName = 'jackie';
let lastName = 'willen';
魔法数字提取为常量
// ❌ 8 是什么意思?
if (value.length < 8) { /* ... */ }
// ✅ 用常量表达业务含义
const MAX_INPUT_LENGTH = 8;
if (value.length < MAX_INPUT_LENGTH) { /* ... */ }
避免啰嗦命名
// ❌
let nameString;
let theUsers;
// ✅
let name;
let users;
复杂表达式拆成有意义的变量
// ❌ 正则结果含义不明
const address = 'One Infinite Loop, Cupertino 95014';
const regex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
address.match(regex)[1],
address.match(regex)[2]
);
// ✅ 解构 + 兜底
const [, city, zipCode] = address.match(regex) || [];
saveCityZipCode(city, zipCode);
求值变量做好兜底
// ❌ fullName 只有一项时会得到 undefined
const lastName = fullName[1];
if (lastName.length > MIN_NAME_LENGTH) { /* ... */ }
// ✅ 兜底为空字符串
const lastName = fullName[1] || '';
if (lastName.length > MIN_NAME_LENGTH) { /* ... */ }
减少全局变量污染
// ❌ 多个文件修改 window.name
// name.js
window.name = 'a';
// hello.js
window.name = 'b';
// ✅ 使用模块作用域或状态管理
// store/user.js
export const userStore = { name: 'a' };
函数
布尔返回值以 is / has / can / should 开头
// ❌ 看不出返回类型
function showFriendsList() { /* ... */ }
// ✅
function shouldShowFriendsList() { return true; }
function isEmpty(list) { return list.length === 0; }
function hasPermission(user) { return user.role === 'admin'; }
优先写纯函数
// ❌ 依赖外部 API,同样入参结果不稳定
function plusAbc(a, b) {
const c = fetch('/api').then(r => r.json());
return a + b + c;
}
// ✅ 输入确定,输出确定
function sum(a, b, c) {
return a + b + c;
}
布尔参数改为对象参数
// ❌ true/false 含义不清
page.getSVG(api, true, false);
// ✅
page.getSVG({ api, animated: true, cache: false });
函数名以动词开头
// ❌
function emlU(user) { /* ... */ }
// ✅
function sendEmailToUser(user) { /* ... */ }
一个函数只做一件事
// ❌ 查询、判断、发送混在一起
function sendEmailToClients(clients) {
clients.forEach(client => {
const record = database.lookup(client);
if (record.isActive()) {
email(client);
}
});
}
// ✅ 拆分职责
function isActiveClient(client) {
return database.lookup(client).isActive();
}
function sendEmailToActiveClients(clients) {
clients.filter(isActiveClient).forEach(email);
}
用 map / filter 替代无意义的 for 循环
// ❌
for (let i = 0; i < a.length; i++) {
a[i] = a[i] + 1;
}
// ✅
const b = a.map(item => item + 1);
减少深层 if-else
// ❌
if (status === 1) { /* ... */ }
else if (status === 2) { /* ... */ }
else if (status === 3) { /* ... */ }
else { /* ... */ }
// ✅ 策略表
const handlers = {
1: handlePending,
2: handleActive,
3: handleClosed,
};
(handlers[status] || handleDefault)();
异步与错误处理
// ❌ 不处理异常
async function loadData() {
const res = await fetch('/api/data');
return res.json();
}
// ✅ 明确错误边界
async function loadData() {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error('loadData failed:', err);
return null;
}
}
组件与文件(Vue / React 通用)
| 规范 | 说明 |
|---|---|
| 单文件单组件 | 一个 .vue / .tsx 对应一个主组件 |
| 文件名 PascalCase | UserProfile.vue、OrderList.tsx |
| Props 声明类型 | Vue 用 defineProps,React 用 TypeScript interface |
| 事件名 kebab-case | Vue:@update-count;React:onUpdateCount |
ESLint 推荐配置
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:vue/vue3-recommended"
],
"rules": {
"no-unused-vars": "error",
"prefer-const": "error",
"no-var": "error",
"eqeqeq": ["error", "always"]
}
}
配合 Prettier 统一格式,提交前通过 lint-staged 自动检查:
{
"lint-staged": {
"*.{js,ts,vue}": ["eslint --fix", "prettier --write"]
}
}
代码审查清单
提交 PR 前自检:
- [ ] 无未使用变量与 import
- [ ] 无魔法数字,业务常量已提取
- [ ] 函数职责单一,命名可读
- [ ] 异步调用有错误处理
- [ ] 无
console.log调试残留 - [ ] ESLint / TypeScript 检查通过
小结
编码规范的核心就三条:命名说清楚、函数做一件事、边界要兜底。团队可以先从 ESLint 规则落地,再逐步补充本文中的约定,比一次性写几百页文档更有效。
评论