好的编码规范不是为了限制创造力,而是让六个月后的自己和同事都能快速读懂代码。本文整理前端日常开发中最容易踩坑的规范,配合 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 对应一个主组件
文件名 PascalCaseUserProfile.vueOrderList.tsx
Props 声明类型Vue 用 defineProps,React 用 TypeScript interface
事件名 kebab-caseVue:@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 规则落地,再逐步补充本文中的约定,比一次性写几百页文档更有效。