浅谈EditorConfig、Prettier以及Eslint的使用
EditorConfig、Prettier以及Eslint都用于实现前端代码规范化的工具,它们的功能分别如下:
EditorConfig: 专注于统一编辑器编码风格配置
Prettier: 专注于检查并自动更正代码风格,美化代码
Eslint: 专注于 JavaScript 代码质量检查, 编码风格约束等
下面将简要介绍一下这三种工具:
目前所有的属性名和属性值都是大小写不敏感的。编译时都会将其转为小写。通常,如果没有明确指定某个属性,则会使用编辑器的配置,而 EditorConfig 不会处理。
editorconfig通配符:
* : 匹配除路径分隔符“/”以外的任意字符
**: 匹配任意字符
?: 匹配任意单个字符
[name]: 指定匹配字符
[!name]: 指定非匹配字符
{s1,s2,s3} : 匹配多个指定字符,以分号“,”分隔
{num1…num2} : 匹配num1和num2之间的任意整数,num1和num2可以是正数,也可以是负数;特殊字符可以用反斜杠""转义,避免被解释为通配符模式
示例配置:
root = true //表示为最顶层的配置文件,不会再向上查找
[*] //对所有文件生效
indent_style = space //设置缩进为空格
indent_size = 2 // 设置缩进所占列数为2
end_of_line = lf //表示\n 转义字符、换行
charset = utf-8 //设置为utf-8编码
trim_trailing_whitespace = true //设置去除行尾的空白字符
insert_final_newline = true //设置文件空白行结尾
[*.md] //以.md结尾的文件生效
trim_trailing_whitespace = false //设置不去除行尾的空白字符
[Makefile] //对Makefile文件生效
indent_style = tab //设置缩进为回车
EditorConfig 解决的只是编辑器配置层面的编码风格一致性问题。对于代码风格的部分并未涉及,比如是否「需要在语句末尾添加分号」,「字符串使用单引号还是双引号包裹」,「多行对象的书写规范」等等。这一类代码风格问题,则是由Prettier来规范。
2. Prettier
Prettier通过解析代码并匹配自己的一套规则,来强制执行一致的代码展示格式,在美化代码方面有很大的优势。Prettier 通过语法分析将代码解析为 AST 树(抽象语法树(Abstract Syntax Tree,AST),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构,在 AST 树上应用代码风格规范重新生成代码。
Prettier 使用 cosmiconfig 支持配置文件,cosmiconfig 是一种常用的配置文件读取工具,按照下述顺序沿文件树寻找配置文件,找到则停止:
package.json 中的 prettier 字段
.prettierrc 文件
.prettierrc.json 文件
.prettierrc.js 文件
.prettierrc.toml 文件
选择上述任一方式进行自定义配置 Prettier,如不存在配置文件,Prettier 将依照默认值处理。
Prettier 处理的范围包含如:
字符串引号风格
空行处理
多行对象格式
分号处理
打印宽度:
控制换行
通过换行控制评论影响范围
示例配置:
printWidth: 80, // 打印宽度,默认是 80 列
tabWidth: 2, // 缩进所占列数,默认是 2 列
useTabs: false, // 缩进风格是否是Tab,默认是 false ,使用空格缩进
semi: true, // 在语句末尾添加分号,默认是 true
singleQuote: false, // 使用单引号,默认是 false
quoteProps: "as-needed", // 对象中的属性使用引号, "as-needed" (默认)只对需要的属性加引号; "consistent" 同一对象中属性引号保持统一;"preserve" 强制使用引号。
jsxSingleQuotes: false, // JSX中使用单引号,默认是 false
trailingComma: "es5", // 多行时是否结尾添加逗号; "es5" (默认)ES5中允许逗号的容器中添加逗号; "all" 尽可能添加逗号;"none" 不允许添加逗
bracketSpacing: true, // 是否保留对象内侧两端的空格,比如 { foo: bar } 和 {foo:bar} 的区别
jsxBracketSameLine: false, // 多行 JSX 的元素是否能和属性同行,在多行JSX元素的最后一行追加 >,默认是 false
arrowParens: "always", // 箭头函数参数使用圆括号包裹 比如 (x) => x 和 x => x 的区别,"always"( 默认) 总是包裹; "avoid" 尽可能避免包裹
rangeStart: 0, // 只格式化文件中的一部分,范围开始于第几行
rangeEnd: Infinity, // 只格式化文件中的一部分,范围结束于第几行
parser: "none", // 指定解析器,根据文件路径推断解析器,比如 .js 文件使用 babel 解析;.scss 文件使用 post-scss 解析
filepath: "none", // 指定用于推断使用那个解析器的文件名
requirePragma: false, // 限制只格式化在文件顶部做了需格式化标识的文件,适用于在大型未格式化项目中,先指定少量文件格式化
insertPragma: false, //在文件的第一个docblock注释中插入@format pragma
proseWrap: "preserve", //默认为’ preserve’, 还有’nerver’和’always’
htmlWhitespaceSensitivity: "css", // HTML 文件的空格敏感度,"css"(默认) 和 css 的 display 属性保持一致;"strict" 空格敏感; "ignore" 空格不敏感
vueIndentScriptAndStyle: false, // 是否对 Vue 文件中 <script> 和 <style> 标签内的代码应用缩进
endOfLine: "lf", // 换行符
embeddedLanguageFormatting: "auto", // 是否格式化嵌入引用代码,比如 markdown 文件中嵌入的代码块; "auto" Prettier 自动识别并格式化; "off" 关闭自动格式化
Prettier 专注于统一代码样式,并不具有 lint 检查语法等能力。而 Eslint 专注于找到代码存在的问题避免错误。
Eslint配置文件:
extends: 指定扩展的配置,配置支持递归扩展,支持规则的覆盖和聚合。extends 数组的顺序非常重要。基本上,每次一个新配置添加到这个数组中,它都会覆盖前面的配置。
ignorePatterns: 忽略特定的文件和目录
rules: 启用的规则及其各自的错误级别,可以覆盖掉extends的配置。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:“off” (0)- 关闭规则;“warn”(1)- 开启规则,使用警告级别的错误: “error” (2)- 开启规则,使用错误级别的错误,此时程序会退出
plugins: 配置想要使用的Linting规则的第三方插件名字列表。插件名称可以省略 eslint-plugin- 前缀。
env: 指定脚本的运行环境,每种环境都有一组特定的预定义全局变量。如 brower、node、es6 等。
parser: 默认Eslint使用Espree作为解析器,如果使用babel的话,我们需要用babel-eslint。
parserOptions: 当默认解析器从Espree改为babel-eslint时,必须指定parseOptions
globals: 配置额外的全局变量。启用 Eslint规则后,当访问当前源文件内未定义的变量时,no-undef 规则将发出警告。 而有时候,我们是需要在其他文件访问一些全局变量的,且保证能正常取到值。这时可以在 Eslint中定义这些全局变量,这样 Eslint就不会发出警告了。
settings:该字段定义的数据可以在所有的插件中共享。这样每条规则执行的时候都可以访问这里面定义的数据
4. EditorConfig、Prettier以及Eslint 之间的协作
Prettier 和 EditorConfig 共享一些配置项,我们不希望在两个单独的文件中重复这些配置项,并让二者保持同步(比如:尾行配置)。Prettier 的最新版本通过解析 .editorconfig 文件来确定要使用的配置选项解决了此问题。
这些选项仅限于:
end_of_line
indent_style
indent_size/tab_width
max_line_length
这些配置选项将覆盖以下 Prettier 选项(如果未在 .prettierrc 中定义它们):
"endOfLine"
"useTabs"
"tabWidth"
"printWidth"
解决prettier和eslint间冲突的插件,有如下几种:
eslint-config-prettier: 会禁用掉所有非必须或者和prettier冲突的规则,以符合eslint规则的方式格式化代码并提示对应的修改建议。
eslint-plugin-prettier: 该插件辅助Eslint可以平滑地与Prettier一起协作,并将Prettier的解析作为Eslint的一部分,在最后的输出可以给出修改意见。这样当Prettier格式化代码的时候,依然能够遵循我们的Eslint规则。
prettier-eslint:可以让prettier和eslint结合起来。输入代码,执行prettier后再eslint --fix输出格式化后的代码。但该工具只支持输入代码字符串,不支持读取文件。
prettier-eslint-cli:在prettier-eslint的基础上支持读取文件,支持CLI命令执行prettier-eslint的操作。
5. Lint-staged和Husky
Lint-staged
Lint-staged帮助你在暂存文件的时候能够让错误格式代码不会提交到你分支。
由于提交代码前的检查是最后一个管控代码质量的一个环节,所以在提交代码之前进行lint检查意义重大。这样可以确保没有错误的语法和代码样式被提交到仓库上。但是在整个项目上执行Lint进程会很低效,所以最好的做法就是检查那个被改动的文件。而Lint-staged就是做这个的。
在package.json中配置该字段:
"lint-staged": {
"*.{tsx,ts}": [ // 这里的文件后缀可以修改成自己需要的文件后缀
"prettier-eslint --write",
"eslint --fix --max-warnings=0 --no-ignore",
"git add"
]
}
通过该配置,去实现由prettier-eslint-cli将代码prettier一遍后,再eslint --fix,如果没有错误,那么就会直接执行git add,否则报错退出。
Husky
为了保证每次commit的时候会执行lint操作,我们需要借助git的钩子功能,而提供钩子功能的就是Husky,该工具提供了git在多个阶段前执行的操作。
在package.json中配置该字段:
"husky": {
"hooks": {
"pre-commit": "lint-staged", //预提交的时候进行Lint检查
"pre-push": "npm run test:unit" //代码推送到远端时运行单元测试
}
},