4. 代码规范与静态检查
在多人协作的 Node.js 项目中,代码风格分歧往往比逻辑错误更消耗沟通成本:有人用 2 空格缩进,有人用 4 空格;有人坚持单引号,有人习惯双引号;有人提交前忘了格式化,导致 PR 里出现大量无意义的空白 diff。代码规范与静态检查的目标,不是限制开发者,而是把“格式与常见错误”这类可自动判定的事情交给工具,让 Code Review 聚焦在业务逻辑、边界条件和架构设计上。
本节要落地的工具体系由四层组成,它们职责不同、互相配合:EditorConfig 负责编辑器层面的基础格式统一(缩进、换行符、文件末尾空行);Prettier 负责代码的格式化输出,只关心“长什么样”;ESLint 负责代码质量与潜在错误检查,关心“写得对不对”;lint-staged 负责在 Git 提交前只对暂存区文件执行上述检查与自动修复,保证进入仓库的代码已经过规范处理。下面这张图展示了四者在一次提交中的协作关系。
各工具职责与配置要点
EditorConfig 是最轻量的一层,它不依赖 Node.js,几乎所有主流编辑器都原生支持或通过插件支持。在项目根目录放置 .editorconfig,即可统一缩进风格、字符编码和换行符,避免 Windows 与 macOS/Linux 协作时出现整文件换行符差异。
# .editorconfig
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
Prettier 是“格式化权威”,它不判断代码对错,只负责把代码排版成统一风格。关键配置项包括 printWidth(默认 80)、tabWidth(默认 2)、semi(是否加分号)、singleQuote(是否用单引号)、trailingComma(尾逗号策略)。在 .prettierrc 中集中声明,避免每个开发者本地设置不同。
{
"printWidth": 100,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "always"
}
ESLint 负责代码质量。在 Node.js 项目中,推荐使用 eslint:recommended 作为基础,再叠加 plugin:node/recommended 和 plugin:prettier/recommended。后者有两个作用:一是把 Prettier 作为 ESLint 规则运行,二是关闭所有与 Prettier 冲突的格式类规则,避免两个工具互相“打架”。
// .eslintrc.js
module.exports = {
env: { node: true, es2022: true },
extends: [
'eslint:recommended',
'plugin:node/recommended',
'plugin:prettier/recommended'
],
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
eqeqeq: ['error', 'always'],
'node/no-unsupported-features/es-syntax': 'off'
}
};
这里有一个常见误区:no-unused-vars 对以 _ 开头的参数放行,是为了兼容 Express 中间件的 (err, req, res, next) 签名中某些未使用的参数。规则不是越严越好,而是要与项目实际写法匹配,否则开发者会用 eslint-disable 注释绕过,规范就形同虚设。
lint-staged 与 Git Hooks 的衔接
lint-staged 本身不监听 Git 事件,它需要配合 Husky 使用。Husky 负责在 pre-commit 阶段触发脚本,lint-staged 则接收暂存文件列表并执行对应命令。这样做的核心价值是只检查本次提交涉及的文件,而不是全量扫描整个仓库,速度从几十秒降到一两秒。
// package.json
{
"scripts": {
"lint": "eslint . --ext .js,.ts",
"format": "prettier --write ."
},
"lint-staged": {
"*.{js,ts}": ["eslint --fix", "prettier --write"],
"*.{json,md,yml}": ["prettier --write"]
}
}
# 安装与初始化
npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier \
eslint-plugin-node husky lint-staged
npx husky init
# 在 .husky/pre-commit 中写入:
npx lint-staged
执行顺序上,先 eslint --fix 再 prettier --write 是推荐做法:ESLint 先处理逻辑类问题并修复可自动修复项,Prettier 最后统一排版。如果顺序反过来,Prettier 格式化后的代码可能再次触发 ESLint 的格式规则,导致重复修改。
@typescript-eslint/parser 和 @typescript-eslint/eslint-plugin 加入配置,并将 lint-staged 的匹配改为 *.{js,ts,tsx}。同时建议在 CI 中增加一条 eslint . 全量检查,防止有人用 --no-verify 跳过本地钩子。团队规范落地建议
工具配置完成后,还需要一份简短的团队约定,否则规则会随人员流动而漂移。建议在仓库根目录维护 CONTRIBUTING.md,明确以下内容:提交前必须通过 npm run lint;禁止使用 git commit --no-verify 绕过检查;新增 ESLint 规则需在 PR 中说明理由;每季度回顾一次规则集,删除长期被 disable 的规则。规范的生命力在于被遵守,而遵守的前提是规则本身合理、可执行、有反馈。
最后需要强调:静态检查不能替代测试。ESLint 能发现未使用变量、未定义引用、== 与 === 混用等常见问题,但无法验证业务逻辑是否正确。把 lint 作为提交前的第一道闸门,把单元测试和集成测试作为第二道闸门,两者配合才能既保证代码整洁,又保证行为可靠。