最近遇到了一个老项目,比较有意思的是这个项目集前后端的代码于一起,而后端也会去修改前端代码,所以就出现了后端用 IntelliJ IDEA 来开发前端项目,而前端用 VSCode 来开发前端项目的情况。于是乎,出现了代码规范的问题,所以就有了这篇文章,整理了一下前端代码校验以及在 Vue 项目中的实践。
创新互联公司专注于企业网络营销推广、网站重做改版、嘉兴网站定制设计、自适应品牌网站建设、HTML5、商城网站建设、集团公司官网建设、成都外贸网站建设公司、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为嘉兴等各大城市提供网站开发制作服务。
阅读完这篇文章,你可以收获:
下面开始阅读吧,如果你对 ESLint 比较熟悉,可以直接跳过这个部分。
ESLint 是一个集代码审查和修复的工具,它的核心功能是通过配置一个个规则来限制代码的合法性和风格。
ESLint 的解析器,早期的时候用的是 Esprima[1],后面基于 Esprima v1.2.2 版本开发了一个新的解析器 Espree[2],并且把它当做默认解析器。
除了使用 ESLint 自带的解析器外,还可以指定其他解析器:
为项目指定某个选择器的原则是什么?
“如果你对 ES 最新标准还不熟悉,可以看看这篇文章:送你一份精心总结的3万字ES6实用指南(下)
除了指定解析器 parser 外,还可以额外配置解析器参数 parserOption:
- {
- // ESLint 默认解析器,也可以指定成别的
- parser: "espree",
- parserOption: {
- // 指定要使用的 ECMAScript 版本,默认值 5
- ecmaVersion: 5,
- // 设置为 script (默认) 或 module(如果你的代码是 ECMAScript 模块)
- sourceType: "script",
- // 这是个对象,表示你想使用的额外的语言特性,所有选项默认都是 false
- ecmafeatures: {
- // 是否允许在全局作用域下使用 return 语句
- globalReturn: false,
- // 是否启用全局 strict 模式(严格模式)
- impliedStrict: false,
- // 是否启用JSX
- jsx: false,
- // 是否启用对实验性的objectRest/spreadProperties的支持
- experimentalObjectRestSpread: false
- }
- }
- }
指定不同的环境可以给对应环境下提供预设的全局变量。比如说在 browser 环境下,可以使用 window 全局变量;在 node 环境下,可以使用 process 全局变量等;
配置方式如下:
- {
- env: {
- browser: true,
- node: true,
- es6: true,
- commonjs: true,
- mocha: true,
- jquery: true,
- }
- }
可以指定多个环境并不意味着配置的环境越多越好,实际配置的时候还是得依据当前项目的环境来选择。
ESLint 的一些核心规则依赖于对代码在运行时可用的全局变量的了解。由于这些在不同环境之间可能会有很大差异,并且在运行时会进行修改,因此 ESLint 不会假设你的执行环境中存在哪些全局变量。
如果你想使用这些全局变量,那就可以通过 globals 来指定。比如在 react .eslintrc.js[6] 里就把 spyOnDev、 spyOnProd 等变量挂在了 global 下作为全局变量:
- {
- globals: {
- spyOnDev: true,
- spyOnProd: true,
- }
- }
对于它的值需要特别说明下:
实际项目中配置规则的时候,不可能团队一条一条的去商议配置,太费精力了。通常的做法是使用业内大家普通使用的、遵循的编码规范;然后通过 extends 去引入这些规范。extends 配置的时候接受字符串或者数组:
- {
- extends: [
- 'eslint:recommended',
- 'plugin:vue/essential',
- 'eslint-config-standard', // 可以缩写成 'standard'
- '@vue/prettier',
- './node_modules/coding-standard/.eslintrc-es6'
- ]
- }
那有哪些常用的、比较著名扩展可以被 extends 引入呢
ESLint 虽然可以定义很多的 rules,以及通过 extends 来引入更多的规则,但是说到底只是检查 JS 语法。如果需要检查 Vue 中的 template 或者 React 中的 jsx,就束手无策了。所以引入插件的目的就是为了增强 ESLint 的检查能力和范围。
ESLint 相关的插件的命名形式有 2 种:不带命名空间的和带命名空间的,比如:
- {
- plugins: [
- 'jquery', // 是指 eslint-plugin-jquery
- '@jquery/jquery', // 是指 @jquery/eslint-plugin-jquery
- '@foobar', // 是指 @foobar/eslint-plugin
- ]
- }
当需要基于插件进行 extends 和 rules 的配置的时候,需要加上插件的引用,比如:
- {
- plugins: [
- 'jquery', // eslint-plugin-jquery
- '@foo/foo', // @foo/eslint-plugin-foo
- '@bar, // @bar/eslint-plugin
- ],
- extends: [
- 'plugin:jquery/recommended',
- 'plugin:@foo/foo/recommended',
- 'plugin:@bar/recommended'
- ],
- rules: {
- 'jquery/a-rule': 'error',
- '@foo/foo/some-rule': 'error',
- '@bar/another-rule': 'error'
- },
- }
以上配置来自 ESLint plugins[10]
ESLint 提供了大量内置的规则,这里是它的规则列表 ESLint Rules,除此之外你还可以通过插件来添加更多的规则。
通常规则只需要配置开启还是关闭即可;但是也有些规则可以传入属性,比如:
- {
- rules: {
- 'quotes': ['error', 'single'], // 如果不是单引号,则报错
- 'one-var': ['error', {
- 'var': 'always', // 每个函数作用域中,只允许 1 个 var 声明
- 'let': 'never', // 每个块作用域中,允许多个 let 声明
- 'const': 'never', // 每个块作用域中,允许多个 const 声明
- }]
- }
- }
如何知道某个扩展有哪些规则可以配置,以及每个规则具体限制?这里直接给出业内著名且使用比较多的规则列表的快速链接:
ESLint 检测配置文件步骤:
通常我们都习惯把 ESLint 配置文件放到项目根目录,因此可以为了避免 ESLint 校验的时候往父级目录查找配置文件,所以需要在配置文件中加上 root: true。
- {
- root: true,
- }
ESLint 支持在配置文件添加共享设置,你可以添加 settings 对象到配置文件,它将提供给每一个将被执行的规则。如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置:
- {
- settings: {
- sharedData: 'Hello'
- },
- }
参考:ESLint配置文件.eslintrc参数说明[11]
比如 webpack 的中包含了某些运行时的 JS 文件,而这些文件是只跑在浏览器端的,所以需要针对这部分文件进行差异化配置:
- overrides: [
- {
- files: ["lib/**/*.runtime.js", "hot/*.js"],
- env: {
- es6: false,
- browser: true
- },
- globals: {
- Promise: false
- },
- parserOptions: {
- ecmaVersion: 5
- }
- }
- ]
以上配置来自 webpack .eslintrc.js[12]
上面细说了 ESLint 的各种配置项,以及针对 Vue 项目如何进行差异配置的说明。
现在我们知道了如何配置,但是你知道这些配置都是配置到哪里的吗?
ESLint 支持 3 种配置方式:
下面通过命令来生成一个配置文件:
- # 安装 eslint
- npm i eslint -D
- # 初始化一个配置文件
- npx eslint --init
最后会在当前目录生成一个 .eslintrc.js 文件。这里就不把代码贴出来了,没参考意义。
上面我们知道了可以将配置统一写到一个配置文件里,但是你知道该如何去触发这个配置文件的校验规则嘛?
- // 校验 a.js 和 b.js
- npx eslint a.js b.js
- // 校验 src 和 scripts 目录
- npx eslint src scripts
通常 ESLint 只能校验 JS 文件。比如需要校验 .vue 文件,光配置 vue 插件和 vue-eslint-parser 解析器是不够的,还需要让 ESLint 在查找文件的时候找到 .vue 文件。
可以通过 --ext 来指定具体需要校验的文件:
- npx eslint --ext .js,.jsx,.vue src
rules 列表项中标识了一个扳手 图案的规则就标识该规则是可以通过 ESLint 工具自动修复代码的。如何自动修复呢?通过 --fix 即可。比如对于 ESLint Rules 里的这个 semi 规则,它就是带扳手图案的。
对于如下的 a.js 代码:
- const num = 12
当在配置文件配置了 'semi': [2, 'always'] 后,运行命令:
- npx eslint --fix a.js
校验直接就通过了,且会自动修复代码,在代码末尾自动加上分号。
检验命令比较长,也难记,习惯上会把这些命名直接写到 package.json 里:
- {
- "scripts": {
- "lint": "npx eslint --ext .js,.jsx,.vue src",
- "lint:fix": "npx eslint --fix --ext .js,.jsx,.vue src",
- }
- }
对于一些公共的 JS、测试脚本或者是特定目录下的文件习惯上是不需要校验的,因此可以在项目根目录通过创建一个 .eslintignore 文件来配置,告诉 ESLint 校验的时候忽略它们:
- public/
- src/main.js
除了 .eslintignore 中指定的文件或目录,ESLint 总是忽略 /node_modules/ 和 /bower_components/ 中的文件;因此对于一些目前解决不了的规则报错,但是如果又急于打包上线,在不影响运行的情况下,我们就可以利用 .eslintignore 文件将其暂时忽略。
上面把 ESLint 的几乎所有的配置参数和校验方式都详细的介绍了一遍,但是如果想在项目中落地,仅仅靠上面的知识还是不够的。下面将细说如何在 Vue 中落地代码校验。
关于如何在 Vue 中落地代码校验,一般是有 2 种情况:
其实这 2 种情况最终的校验的核心配置都是一样的,只是刚开始的时候安装的包有所区别。下面通过分析 vue-cli 配置的代码校验,来看看它到底做了哪些事情,通过它安装的包以及包的作用,我们就会知道如何在空项目中配置代码校验了。
如果你的项目最初是通过 vue-cli 新建的,那么在新建的时候会让你选
如果都开启了话,会安装如下几个包:
下面重点介绍 @vue/cli-plugin-eslint 和 eslint-plugin-vue,说下这 2 个包是干嘛的。
这个包它主要干了 2 件事情:
往 package.json 里注册了一个命令:
- {
- "scripts": {
- "lint": "vue-cli-service lint"
- }
- }
执行这个命令之后,它会去检查和修复部分可以修复的问题。默认查找的文件是 src 和 tests 目录下所有的 .js,.jsx,.vue 文件,以及项目根目录下所有的 js 文件(比如,也会检查 .eslintrc.js)。
当然你也可以自定义的传入参数和校验文件:
- vue-cli-service lint [options] [...files]
支持的参数如下:
增加了代码保存触发校验的功能 lintOnSave,这个功能默认是开启的。如果想要关闭这个功能,可以在 vue.config.js 里配置,习惯上只开启 development 环境下的代码保存校验功能:
- module.exports = {
- lintOnSave: process.env.NODE_ENV === 'development',
- }
lintOnSave 参数说明:
eslint-plugin-vue 是对 .vue 文件进行代码校验的插件。
针对这个插件,它提供了这几个扩展
各扩展规则列表:vue rules
代码规范的东西,原则还是得由各自的团队去磨合商议出一套适合大家的规则。不过,如果你用的是 Vue2,我这里可以推荐 2 套 extends 配置:
- {
- // Vue 官方示例上的配置
- extends: ['eslint:recommended', 'plugin:vue/recommended'],
- // 或者使用 AlloyTeam 团队那套
- extends: ['alloy', 'alloy/vue']
- }
如果是 Vue 2.x 项目,配置了 eslint-plugin-vue 插件和 extends 后,template 校验还是会失效,因为不管是 ESLint 默认的解析器 Espree 还是 babel-eslint 都只能解析 JS,无法解析 template 的内容。
而 vue-eslint-parser 只能解析 template 的内容,但是不会解析 JS,因此还需要对解析器做如下配置:
- {
- parser: 'vue-eslint-parser',
- parseOptions: {
- parser: 'babel-eslint',
- ecmaVersion: 12,
- sourceType: 'module'
- },
- extends: [
- 'eslint:recommended',
- 'plugin:vue/recommended'
- ],
- plugins: ['vue']
- }
参考:eslint-plugin-vue faq[13]
针对 Prettier 不得不提出以下疑问?
用它自己的话来说:我是一个自以为是的代码格式化工具,而且我支持的文件类型很多,比如:
以及还有一些其他类型的文件。
我们知道 ESLint 负责了对代码的校验功能,并且主要提供了 2 类规则:
说到底 ESLint 就是通过一条条的规则去限制代码的规范,但是这些规则毕竟是有限的,而且更重要的是这些规则的重点并不在代码风格上,所以单凭 ESLint 并不能完全的统一代码风格。
这个时候就需要引入 Prettier 了,因为它干的事就是只管代码格式化,不管代码质量。
“Prettier:在代码风格这一块,我一直拿捏的死死的。
初始化操作:
- # 安装包
- npm i prettier -D
- # 新建 .prettierrc.js
- echo module.exports = {} > .prettierrc.js
- # 新建 .prettierignore
- echo > .prettierignore
Prettier 支持可以配置参数不多,总共才 21 个,这里是所有参数的说明 prettier options[14]
所有参数都有默认值,也就是说即使你没有配置 .prettierrc.js,当你用 Prettier 去格式化代码的时候全部都会走默认配置。针对个别参数,你不想用默认设置的话,就可以在 .prettierrc.js 配置具体想要的值。
如下,把项目中会用到的参数进行一个说明:
- module.exports = {
- printWidth: 80, //(默认值)单行代码超出 80 个字符自动换行
- tabWidth: 2, //(默认值)一个 tab 键缩进相当于 2 个空格
- useTabs: true, // 行缩进使用 tab 键代替空格
- semi: false, //(默认值)语句的末尾加上分号
- singleQuote: true, // 使用单引号
- quoteProps: 'as-needed', //(默认值)仅仅当必须的时候才会加上双引号
- jsxSingleQuote: true, // 在 JSX 中使用单引号
- trailingComma: 'all', // 不用在多行的逗号分隔的句法结构的最后一行的末尾加上逗号
- bracketSpacing: true, //(默认值)在括号和对象的文字之间加上一个空格
- jsxBracketSameLine: true, // 把 > 符号放在多行的 JSX 元素的最后一行
- arrowParens: 'avoid', // 当箭头函数中只有一个参数的时候可以忽略括弧
- vueIndentScriptAndStyle: false, //(默认值)对于 .vue 文件,不缩进