用 ESLint 9 的 Flat Config,把团队代码规范“焊死”在 CI 里
不知道你有没有遇到过这种场景每次 Code Review大家一半时间在争论到底该用单引号还是双引号要不要加分号缩进是 2 格还是 4 格。代码还没看逻辑先被风格问题气得血压飙升。讲真这种内耗完全可以交给工具来解决。ESLint 就是那个帮你把“代码风格”从玄学变成工程的利器。今天咱们就基于 ESLint 9 的 Flat Config从零到一搭一套能直接落地的代码检查方案。一、先跑起来初始化 ESLint如果你用的是 ESLint 9 及以上版本初始化非常简单npm install -D eslint eslint/js globals npx eslint --initnpx eslint --init会引导你选择项目类型、模块方案、是否使用 TypeScript 等最后生成一个eslint.config.mjs文件。不过说实话自动生成的配置往往不够贴合团队需求咱们不如直接手写一个清晰的配置以后维护也方便。项目根目录新建eslint.config.mjsimport js from eslint/js; import globals from globals; import { defineConfig } from eslint/config; export default defineConfig([ js.configs.recommended, { files: [**/*.{js,mjs,cjs}], languageOptions: { globals: globals.node }, rules: { no-var: 2, no-console: 1, quotes: [error, double], semi: [error, always], indent: [error, 2] } } ]);如果你之前用旧版 ESLint 的.eslintrc格式可能会对defineConfig和数组结构感到陌生。这就是 Flat ConfigESLint 9 的主推方式配置文件本身也是一个 ES Module类型提示和复用性都更好。二、拆解配置文件每个字段都在干什么别小看上面这 20 行代码它基本涵盖了 Flat Config 的核心要素。1.js.configs.recommendedeslint/js是 ESLint 官方提供的核心规则包。js.configs.recommended就是官方推荐的一组规则包含了no-unused-vars、no-undef、no-dupe-keys等最容易踩坑的检查。你会发现有了它很多低级错误在写代码阶段就能被揪出来。2.files: [**/*.{js,mjs,cjs}]这个配置块只对.js、.mjs、.cjs文件生效。Flat Config 最大的好处就是可以针对不同文件类型做精细化的规则覆盖比如后面给测试文件关掉no-console也很方便。3.languageOptions: { globals: globals.node }声明全局变量。因为我们是 Node.js 项目所以用globals.node。如果你写的是浏览器代码记得换成globals.browser否则 ESLint 会误报document、window未定义。4.rules这里就是自定义规则区也是团队规范真正落地的地方。下面详细说。三、规则的三档力度0/1/2 的哲学ESLint 规则值有三种0 / off关闭规则完全不检查1 / warn警告不阻断流程但会在终端黄字提示2 / error错误会直接让 lint 失败通常用于强制约束说白了就是error是底线必须改warn是建议有空就处理off是不关心留给团队自由发挥。我在配置里挑了四条最经典的规则rules: { no-var: 2, // 禁用 var强制 let/const no-console: 1, // 开发时警告生产环境需要移除 quotes: [error, double], // 强制双引号 semi: [error, always], // 语句末尾必须加分号 indent: [error, 2] // 缩进必须为 2 个空格 }no-var设为error因为var存在变量提升、作用域混乱等问题现代 JavaScript 已经没有理由再用它。no-console设为warn因为在开发调试时难免要打日志但提交到生产前应该清理掉。quotes和semi属于纯风格问题统一后能减少大量无意义的 diff 争论。indent设为 2 个空格这是目前前端社区的主流选择Node.js 项目也基本默认。这里有一点要特别注意规则值不是数组时直接写数字需要配置参数时用数组第一个元素是等级后面是配置。比如[error, double]就是错误级别 双引号选项。四、把 lint 变成肌肉记忆npm scripts配置写好了但没人天天手动敲npx eslint .。把它接入 npm scripts 才是正道scripts: { lint: eslint ., lint:fix: eslint --fix ., test: echo Error: no test specified exit 1 }以后团队里只需要记两个命令npm run lint # 只检查不修改文件 npm run lint:fix # 检查并自动修复能修的问题lint:fix能帮你自动补分号、统一引号、调整缩进但像no-var这种需要语义判断的规则它不会自动帮你把var改成let因为 ESLint 不知道你的真实意图是什么。自动修复是帮你省力不是替你思考。五、实战一个典型违规文件的救赎光说不练假把式。我们建一个index.mjs故意写一些违规代码var name lgl function hello() { console.log(hello) } hello()运行npm run lint你会看到一堆报错/path/to/index.mjs 1:1 error Unexpected var, use let or const instead no-var 1:14 error Strings must use doublequote quotes 1:18 error Missing semicolon semi 3:1 error Expected indentation of 2 spaces but found 4 indent 4:1 error Expected indentation of 2 spaces but found 4 indent 5:1 error Expected indentation of 2 spaces but found 4 indent现在我们试一下npm run lint:fixnpm run lint:fix再次打开文件你会发现引号、分号、缩进全部自动修好了但var还在var name lgl; function hello() { console.log(hello); } hello();这时终端会提示no-var仍然报错因为--fix只能修复确定性规则。你需要手动把var name改成let name然后再跑一次 lint就全部通过了。let name lgl; function hello() { console.log(name hello); } hello();这就是一个完整的“发现违规 → 自动修复 → 手动处理剩余问题 → 通过检查”的闭环。你会发现ESLint 的价值不在于让代码看起来整齐而在于把团队的编码共识沉淀为机器可以执行的命令。六、团队落地的几个实用建议1. 配合 husky lint-staged 在提交前检查光有 npm scripts 还不够总有人会忘记执行。用husky和lint-staged在git commit时自动跑 lint只有通过检查才能提交。这样就把规范从“自觉”变成了“强制”。// package.json 里配置 lint-staged lint-staged: { *.{js,mjs,cjs}: eslint --fix }2. 在 CI 流程中加一道 lint 关卡GitHub Actions、GitLab CI 或 Jenkins 里加一步npm run lint如果返回非零状态码就阻断合并。这样即使有人本地跳过了 husky代码也进不了主干。3. 规则分层别把 warn 当 errorerror是底线必须改warn是建议可以暂缓处理。不要把no-console设成error否则开发调试时你会被自己逼疯。但可以通过overrides在生产构建文件里把它提到error{ files: [**/*.prod.js], rules: { no-console: 2 } }4. 不要盲目继承网上的“全家桶”配置网上的airbnb、standard等预设虽然全面但未必适合你的项目。规则不是越多越好有些规则过于严格反而会降低开发效率。先用recommended兜底再根据团队实际痛点逐步增加规则才是可持续的工程化路线。七、写在最后ESLint 不是用来限制你的工具而是帮你把团队的编码共识变成一条条明确的红线。它让你不必在 Code Review 时为了一个分号浪费口舌也让新同学能快速对齐项目的代码风格。规范不是束缚而是让团队新人少踩坑的护栏。