Linters 及其周边工具使用方式
本文并非旨在介绍 Linters 及其周边工具的具体使用方式,而是详细介绍它们之间的关系和基本原理,并探究更优雅的配合方式。这一切都先从 ESLint 开始:
ESLint
ESLint 是现在最流行 JS Linter,它的运行原理是通过解析器将源代码解析成 AST,ESLint 在遍历 AST 过程中会遍历到每个选择器,ESLint 每条规则定义时都会返回一个或者多个以选择器为 key 的回调函数,选择器被遍历到则会执行对应的回调以检测代码是否符合规则。
通常以配置文件的形式配置规则:
parser
: 解析器,默认使用 Espree 。如果你使用了 Babel 或者 TypeScript,则需要配置对应的解析器完成代码解析,比如babel-eslint
、@typescript-eslint/parser
。extends
: 开启一系列规则。比如设置eslint:all
将开启 ESLint 所有规则,eslint:recommended
则仅开启eslint
官方推荐的一些 规则 。也可以安装并使用更严格的社区规则,比如 airbnb 。rules
: 修改单条规则的错误级别或关闭规则。
// .eslintrc.json { "parser": "babel-eslint", "extends": "eslint:recommended", "rules": { "no-console": "off", // 关闭 "禁用 console" 规则 "no-unused-vars": "error", // 出现未使用过的变量将发出 error 级别的错误,错误会导致程序退出 "max-len": ["warn", { "code": 80 }] // 强制一行的最大长度不得超过 80 个字符,否则将发出 warn 级别的错误,不会导致程序退出 }, // ... }
配置解析器并开启一些检查规则后即可对 JavaScript 代码进行检查。
"scripts": { "lint:script": "eslint --ext '.js,.jsx' src", // 检查指定目录的特定扩展名文件 "lint-fix:script": "npm run lint:script -- --fix", // `--fix` 选项开启自动修复 "lint": "npm run lint-fix:script && lint-fix:style", }
ESLint 中的两类规则
由于 ESLint 中存在两类规则:
- 代码质量规则。如
no-unused-vars
(禁止出现未使用过的变量)。 - 代码格式规则 。如
max-len
(每行代码最大长度)、comma-style
(逗号风格)、keyword-spacing
(关键字前后空格)等。
所以执行 eslint .
命令除了会检测出代码质量问题,还会报告代码格式问题。前者是它最核心的功能。
ESLint 在有 --fix
选项之前 ESLint 不能自动修复格式问题,因为代码格式并不存在错误或潜在的问题,但这类错误却往往非常多,为了解决这一个头疼的问题,引入了 Prettier ,它专门用于统一代码风格。当 ESLint 有了 --fix
之后依然不能完全替代 Prettier
。
Prettier 与 ESLint
ESLint 不能完全替代 Prettier
的原因在于 Prettier 不仅仅可以是 JavaScript 的代码格式化工具,它还支持格式化 JSX
、 Vue
、 TypeScript
、 CSS
、 Less
、 SCSS
、 HTML
、 Markdown
...
如果你想统一项目中所有代码的编码风格,ESLint 能做到的并不全面,所以通常是 ESLint 捕获 JS 代码错误,Prettier 格式化代码(包括 JS 的部分)。下面是 Prettier 的配置文件:
// .prettierrc { "arrowParens": "avoid", // 箭头函数参数只有一个时省略小括号 "singleQuote": true, // 使用单引号代替双引号 "semi": true // 句尾添加分号 }
eslint-config-prettier
由于 ESLint 存在 代码格式规则 ,而且其中部分规则与 Prettier 中的规则存在冲突。
当确定了 ESLint 捕获 JS 代码错误、Prettier 格式代码的分工原则之后,ESLint 中的代码格式规则已经不再必要, eslint-config-prettier
的作用就是关闭这些不必要且可能会与 Prettier 发生冲突的规则:
// .eslintrc { "extends": ["eslint:recommended", "prettier"] // prettier 写在最后才能确保关闭规则 }
eslint-config-prettier
是一个预设配置,是多个规则的集合,原理就是将这些规则全部设置为设置 0
或 "off"
,添加到 extends
选项的尾部实现覆盖。由于这些规则并不影响代码质量的检测,所以覆盖后不会有问题。
module.exports = { rules: { // The following rules can be used in some cases. See the README for more information. // (These are marked with `0` instead of `"off"` so that a script can distinguish them.) "curly": 0, "lines-around-comment": 0, "max-len": 0, "no-confusing-arrow": 0, "no-mixed-operators": 0, "no-tabs": 0, "no-unexpected-multiline": 0, "quotes": 0, "@typescript-eslint/quotes": 0, "babel/quotes": 0, "vue/html-self-closing": 0, "vue/max-len": 0, // The rest are rules that you never need to enable when using Prettier. "array-bracket-newline": "off", // ... } }
eslint-plugin-prettier(不推荐)
关闭 ESLint 的代码格式规则之后,JS 代码的格式化就全部交由 Prettier。但会有些人期望 JS 代码质量和代码格式的错误都通过 ESLint 来报告, eslint-plugin-prettier
的作用就在于此。
插件增加了一条 prettier/prettier
规则,并使用 prettier 对代码风格进行检查,先使用 Prettier 对代码进行格式化,并与格式化前的代码进行对比,出现不一致的地方就会被 Prettier 标记。若 prettier/prettier
规则开启则 Prettier 并将错误信息通过 ESLint 报告。
eslint-plugin-prettier
同样需要预先关闭 ESLint 中的代码格式规则,所以需要配合 eslint-config-prettier
一起使用,因此它附带了一个 plugin:prettier/recommended
配置,通过此配置可以一次性完成与 eslint-config-prettier
的配置:
// .eslintrc { "extends": ["plugin:prettier/recommended"] } // 相当于
{ "extends": ["prettier"], "plugins": ["prettier"], "rules": { "prettier/prettier": "error", // 开启 Prettier 中规则的错误报告 // 使用 eslint-plugin-prettier 若开启下面两条规则可能导致的问题: https://github.com/prettier/eslint-plugin-prettier/issues/65 "arrow-body-style": "off", "prefer-arrow-callback": "off" } }
不推荐的原因:
个人认为不推荐的最主要原因是第一点,实际开发中通常希望能及时发现代码中的质量问题,而不是等到代码提交时再发现并需要进行修改甚至改造,因为这会导致需要重新测试代码逻辑。所以通常的做法是使用 IDE 插件或者构建工具插件实时报告代码质量规则问题,如 VSCode ESLint 插件 )和 eslint-webpack-plugin
。而代码格式问题并不应该在开发过程中看到的,如果集成 eslint-plugin-prettier
将导致编辑器中或控制台中出现很多无关紧要的代码风格提示,这十分影响开发体验。所以对于代码格式问题最好的办法不是需要实时面对并修复它们,而是能忘记代码的格式化。
VSCode ESLint 插件 :读取目录下的 ESLint 配置文件,对代码进行检查后提示错误,可通过配置开启文件保存时自动修复(但没必要)。
tslint-config-prettier、tslint-plugin-prettier
- tslint-config-prettier :禁用所有可能与 prettier 导致冲突的规则。
- tslint-plugin-prettier :将 Prettier 规则作为 TSLint 规则运行,并通过 TSLint 上报问题。
stylelint 场景下也存在一个预设+插件,同样是解决冲突和在 lint 中调用 Prettier,后面会提到。
prettier-eslint、prettier-tslint、prettier-stylelint
有人想到在 ESLint 调用 Prettier( eslint-plugin-prettier
),就有人想用 Prettier 调用 ESLint,于是就有了 prettier-eslint
。
prettier-eslint
解决的问题是:如果没有禁用 ESLint 中的代码格式规则,Prettier 格式化后的代码很可能无法通过 ESLint。所以通常的做法是先使用 prettier --write
格式化后再经过 eslint --fix
处理。 prettier-eslint
的作用就是将这两个步骤合二为一,原理就是将被 Prettier 格式化后的代码传递给 ESLint 执行 eslint --fix
。对于 ESlint 无法处理的扩展名文件则只运行 Prettier,如 .css
, .less
, .scss
或 .json
。
同样的思路诞生了 prettier-tslint 、 prettier-stylelint 。
Git Hooks
上面提到处理代码格式问题的最好的办法是忘记代码的格式化。通常的做法是代码提交时,利用 Git 钩子在提交前自动修复代码格式问题。
Git Hooks 保证 Git 在特定的重要动作发生时触发自定义脚本。项目 git init
之后将初始化一个 .git
目录,在 .git/hooks
目录下默认会填充很多后缀为 .sample
的示例 shell 脚本,这些就是对应钩子执行的 shell 样本文件,将文件名中的后缀 .sample
去掉后即可执行,你可以可以自定义脚本内容并正确命名放入 .git/hooks
目录下。这些脚本除了本身可以被调用外,还透出了被触发时所传入的参数。
常用的 Git Hooks:
pre-commit
:git commit
执行前触发。通常用于检查代码风格是否一致,如 lint 程序。commit-msg
:git commit
执行前触发。通常用于校验提交信息是否规范。- 更多参考
husky
在 .git/hooks
目录下添加 shell 脚本并非特别方便,所以 husky
的作用就是让我们更简单的在项目中添加 Git Hooks,它只需要像添加 npm 脚本一样使用 Git Hooks。
husky@4
husky@4
及其之前的版本,你只需要在 package.json
中加入类似如下配置即可在代码提交时自动完成代码的格式化:
// package.json "husky": { "hooks": { "pre-commit": "lint-staged", "commit-msg": "commitlint -e $GIT_PARAMS", // 校验 commit message 是否规范 } }, // lint-staged 允许我们仅对 git 暂存区的文件上执行特定脚本 "lint-staged": { "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [ "prettier --write" ] }
husky@4
的工作原理是在 .git/hooks
下创建所有类型的 Git Hooks 和一个 husky.sh
脚本,所有 Git Hooks 执行脚本的内容都是执行 husky.sh
(内容如下)。 husky.sh
的程序内容就是去检查用户在 package.json#husky.hooks
是否配置了对应的钩子,有则执行。这也导致 Git 每个特定动作都将执行 husky 设置的脚本,即使用户没有设置任何 Git Hooks。
# .git/hooks/pre-commit #!/bin/sh # husky . "$(dirname "$0")/husky.sh"
这个问题并不好解决,如果不事先创建所有类型的 Git Hooks,用户在后续添加或者删除 package.json#husky.hooks
的配置时,husky 没有好的办法做到同步删除或者添加 .git/hooks
目录下的 hook。
只有用户 hooks 配置和 Git Hooks 在同一个地方才能解决同步问题。
新版本 husky
Git 2.9 引入了 core.hooksPath
的新功能,它允许指定 Git Hooks 执行脚本的存放的目录,而不再必须使用 .git/hooks
目录。这就可以解决了 husky@4
存在的问题。新版的 husky 使用 husky install
命令将 Git Hooks 目录指定为 .husky/
,使用 husky add
命令向 .husky/
中添加 hook。用户后续直接操作 .husky/
目录的 hook 即完成增删改操作,也就不再有同步问题。
添加钩子:
# 创建 pre-commit 钩子 npx husky add .husky/pre-commit "npx lint-staged" # 创建 commit-msg 钩子 npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
package.json
配置:
"scripts": { "prepare": "husky install", // prepare 会在 `npm install` 后自动执行 // ... }, "lint-staged": { "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [ "prettier --write", "git add ." ] }
pretty-quick 和 lint-staged
通常情况下我们并不希望对项目所有代码进行格式化,这会导致当前提交存在大量的修改难以 Code Review;格式化工具还可能存在未知 bug 导致无效代码,这类问题除非大范围回归否则难以发现。所以我们更希望仅对当前修改的代码文件进行格式化。 pretty-quick
和 lint-staged
的作用就是允许我们仅对 Git 暂存区的文件上执行特定脚本,他们通常都与 husky
配合使用。
pretty-quick
pretty-quick
是对更改的文件运行 Prettier,本身仅格式化暂存区的文件。
# 使用 husky@7 添加 pre-commit hook npx husky add .husky/pre-commit "npx --no-install pretty-quick --staged"
--staged
的作用是格式化后的代码会重新暂存,即相当于执行了 git add .
。执行上述命令后
#!/bin/sh . "$(dirname "$0")/_/husky.sh" npx --no-install pretty-quick --staged
lint-staged
lint-staged
并不与 Prettier 存在绑定关系,所以需要在 package.json#lint-staged
字段添加格式化命令。
# 使用 husky@7 添加 pre-commit hook npx husky add .husky/pre-commit "npx --no-install lint-staged"
"lint-staged": { "*.{js,json,yml,yaml,css,scss,ts,tsx,md}": [ "prettier --write", "git add ." ] }
Stylelint
Stylelint 顾名思义是样式代码规范校验工具。类似 ESLint
它同样包含代码质量和代码格式两类规则,使用 --fix
选项自动修复检测出来的问题。下面是一简单的配置文件:
// .stylelintrc.js module.exports = { extends: [ 'stylelint-config-standard', 'stylelint-config-rational-order', 'stylelint-config-prettier', ], ignoreFiles: ['{es,dist,browser}/**/*'], };
stylelint-config-standard
: 官网推荐的 CSS 标准。stylelint-config-recess-order
: 属性排列顺序,保证代码更好地可读性。stylelint-config-prettier
: 为了解决 Prettier 与 Stylelint 的规则冲突,关闭 Stylelint 中所有代码格式规则。放在extends
选项最后以确保覆盖其他配置。
使用:
"scripts": { "lint:style": "stylelint src/**/*.{css,sass,scss,less} --fix" }
VSCode Stylelint 插件 :读取 Stylelint 配置文件,对代码进行检查后提示错误,可通过配置开启文件保存时自动修复(但没必要)。
stylelint-prettier(不推荐)
类似 eslint-plugin-prettier
, stylelint-prettier
的作用也是将 prettier 代码风格规则应用于 stylelint。同样的,它最好与 stylelint-config-prettier
配合使用。
EditorConfig 与 Prettier
EditorConfig 有助于同一项目下使用不同 IDE 的多个开发人员维护一致的编码风格。
# .editorconfig root = true # 不再往上搜索 .editorconfig [*] charset = utf-8 indent_size = 2 indent_style = space insert_final_newline = true # 尾部是否插入一行 trim_trailing_whitespace = true # 是否移除行末的空白字符
EditorConfig 还存在一些与 Prettier 冲突的配置项,应避免出现重复配置。 Prettier
会解析 .editorconfig
文件,并将仅限以下配置项转换为 Prettier 中对应的默认配置,覆盖的前提是 Prettier 配置文件中配置对应选项:
end_of_line
: 对应 Prettier 中的endOfLine
,指定行结束符。indent_style
: 对应useTabs
,表示是否使用 tab 缩进。indent_size
/tab_width
: 对应tabWidth
,表示 tab 缩进大小。max_line_length
: 对应printWidth
,表示每行最大字符数。
commitlint
commitlint 也是 Linters 家族中的一员,用于校验 commit message 是否符合提交格式(通常为 type(scope?): subject
)。
# 安装 commitlint 相关包 npm install --save-dev commitlint @commitlint/config-conventional @commitlint/cli # 创建 commitlint 配置文件,并添加基本配置内容 echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js # 测试是否生效 echo "foo: some message" | commitlint # 失败 echo "fix: some message" | commitlint # 通过 # 添加 commit-msg 钩子 npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
参考
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 虚拟列表原理及其实现
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论