4. 代码规范与静态检查

在多人协作的 Node.js 项目中,代码风格分歧往往比逻辑错误更消耗沟通成本:有人用 2 空格缩进,有人用 4 空格;有人坚持单引号,有人习惯双引号;有人提交前忘了格式化,导致 PR 里出现大量无意义的空白 diff。代码规范与静态检查的目标,不是限制开发者,而是把“格式与常见错误”这类可自动判定的事情交给工具,让 Code Review 聚焦在业务逻辑、边界条件和架构设计上。

本节要落地的工具体系由四层组成,它们职责不同、互相配合:EditorConfig 负责编辑器层面的基础格式统一(缩进、换行符、文件末尾空行);Prettier 负责代码的格式化输出,只关心“长什么样”;ESLint 负责代码质量与潜在错误检查,关心“写得对不对”;lint-staged 负责在 Git 提交前只对暂存区文件执行上述检查与自动修复,保证进入仓库的代码已经过规范处理。下面这张图展示了四者在一次提交中的协作关系。

提交前静态检查与自动修复链路 开发者编辑代码 VS Code / WebStorm EditorConfig 缩进/换行符/编码 git add 暂存区 staged lint-staged 仅处理暂存文件 ESLint no-unused-vars / no-undef eqeqeq / no-console --fix 自动修复可修复项 Prettier 引号/分号/换行 printWidth / tabWidth --write 直接改写 检查是否通过? exit code = 0 ? 通过 提交成功 失败 提交被拦截 修复后重新 add

各工具职责与配置要点

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,把 @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 作为提交前的第一道闸门,把单元测试和集成测试作为第二道闸门,两者配合才能既保证代码整洁,又保证行为可靠。