不知道你有没有遇到过这种场景:每次 Code Review,大家一半时间在争论到底该用单引号还是双引号,要不要加分号,缩进是 2 格还是 4 格。代码还没看逻辑,先被风格问题气得血压飙升。
讲真,这种内耗完全可以交给工具来解决。ESLint 就是那个帮你把“代码风格”从玄学变成工程的利器。 今天咱们就基于 ESLint 9 的 Flat Config,从零到一搭一套能直接落地的代码检查方案。
一、先跑起来:初始化 ESLint
如果你用的是 ESLint 9 及以上版本,初始化非常简单:
npm install -D eslint @eslint/js globals
npx eslint --init
npx eslint --init 会引导你选择项目类型、模块方案、是否使用 TypeScript 等,最后生成一个 eslint.config.mjs 文件。不过说实话,自动生成的配置往往不够贴合团队需求,咱们不如直接手写一个清晰的配置,以后维护也方便。
项目根目录新建 eslint.config.mjs:
import 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 Config,ESLint 9 的主推方式,配置文件本身也是一个 ES Module,类型提示和复用性都更好。
二、拆解配置文件:每个字段都在干什么
别小看上面这 20 行代码,它基本涵盖了 Flat Config 的核心要素。
1. js.configs.recommended
@eslint/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:fix:
npm 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 当 error
error 是底线,必须改;warn 是建议,可以暂缓处理。不要把 no-console 设成 error,否则开发调试时你会被自己逼疯。但可以通过 overrides 在生产构建文件里把它提到 error:
{
files: ["**/*.prod.js"],
rules: {
"no-console": 2
}
}
4. 不要盲目继承网上的“全家桶”配置
网上的 airbnb、standard 等预设虽然全面,但未必适合你的项目。规则不是越多越好,有些规则过于严格反而会降低开发效率。先用 recommended 兜底,再根据团队实际痛点逐步增加规则,才是可持续的工程化路线。
七、写在最后
ESLint 不是用来限制你的工具,而是帮你把团队的编码共识变成一条条明确的红线。它让你不必在 Code Review 时为了一个分号浪费口舌,也让新同学能快速对齐项目的代码风格。
规范不是束缚,而是让团队新人少踩坑的护栏。