ESLint 是什么
- Lint是一种静态代码分析工具,用于检查代码的 语法是否正确、风格是否符合要求。
- ESLint是ECMAScript/JavaScript的Lint工具,用来确保团队代码风格的一致性和避免错误。JSLint和JSHint也是类似的工具。
JSLint,JSHint和ESLint的对比
JSLint
优点
- 配置是已经定好的,开箱即用。
不足
- 有限的配置选项,很多规则不能禁用
- 规范严格,并且遵循的规范比较久远
- 扩展性差
- 无法根据错误定位到对应的规则
- 文档不是很友好,看了官网就懂了
JSHint
优点
- 有了很多参数可以配置
- 支持配置文件,方便使用
- 支持了一些常用类库
- 支持了基本的ES6
不足
- 不支持自定义规则
- 无法根据错误定位到对应的规则
ESLint
优点
- 默认规则里面包含了JSLint和JSHint的规则,易于迁移
- 可配置为警告和错误两个等级,或者直接禁用掉
- 支持插件扩展
- 可以自定义规则
- 可以根据错误定位到对应的规则
- 支持ES6
- 唯一一个支持JSX的工具
不足
- 需要进行一些自定义配置(当然也有社区现成的规范)
- 慢 (它比其他两个都要慢)
热度对比
ESLint的使用
本文环境
- Node.js: 14.3.0 (官方要求>=6.14)
- npm: 6.14.5 (官方要求>3)
- "eslint": "^7.19.0"
安装
注意:由于Node的require功能行为,全局安装的ESLint实例只能使用全局安装的ESLint插件,本地安装的版本只能使用本地安装的插件。不支持混合本地和全局插件。
// 全局安装
// 但是,你使用的任何插件或可共享配置都必须安装在本地
npm i eslint -g
// 本地安装(推荐)
npm i eslint -D
生成配置文件
// 使用本地eslint
npx eslint --init
自定义配置
- 使用 JavaScript 注释把配置信息直接嵌入到一个代码源文件中
- 使用配置文件
.eslintrc.*文件为根目录和子目录指定配置信息,或者直接在package.json文件里的eslintConfig字段指定配置,ESLint 会查找和自动读取它们 - 使用配置文件
.eslintignore,ESLint 去忽略特定的文件和目录
源文件(.js)中通过注释配置规则
/* eslint-disable */ // 取消eslint检测
/* global var1:false, var2:false */ // 指定全局变量
// ...
使用配置文件.eslintrc.*
- 默认的配置文件名为
.eslintrc.*, 可以使用eslint -c customESLint.js指定配置文件 - 支持三种格式的设置:javascript(.eslintrc.js)、yaml(.eslintrc.yml)、json(.eslintrc.json)
- 存在多个配置文件的优先级
- 子目录中的配置文件 > 根目录中的
- .eslintrc.js > .eslintrc.yml > .eslintrc.json > package.json中的
eslintConfig
- 使用 eslint cli自动生成配置文件
.eslintrc.*
// 全局安装的eslint可以直接--init
eslint --init
// 本地安装的eslint,通过npx
npx eslint --init
// 选择规则
To check syntax only // 检查语法
> To check syntax and find problems // 检查语法、发现问题
To check syntax, find problems, and enforce code style // 检查语法、发现问题并强制执行代码样式
// ...
- 也可以直接手动新建
.eslintrc.*配置文件(不推荐)
使用配置文件.eslintignore
/node_modules/
vue.js
vue.min.js
jquery.js
jquery.min.js
// ...
ESLint 配置项
配置文件中的设置都会覆盖默认设置
// .eslintrc.js
module.exports = {
"root": true, // 停止在父级目录中寻找
// 指定环境
"env": {
"browser": true, // 浏览器全局变量
"es6": true, // 启用除模块外的所有ECMAScript 6功能(这会自动将ecmaVersion解析器选项设置为6)
"node": true, // Node.js全局变量和Node.js范围
// ...
},
// 指定全局变量
// 1. 将每个全局变量名称设置为等于以true允许覆盖变量或false禁止覆盖
// 2. 还支持在单独的js文件中通过注释配置全局变量 `/* global var1:false, var2:false */`
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly",
"ENV": true
},
// 指定解析器 - 可以自定义解析器
"parser": "esprima", // 默认情况下,ESLint使用Espree作为其解析器
// 配置解析器选项
"parserOptions": {
"ecmaVersion": 2018, // ECMAScript的版本,2018(与9相同)。默认为ECMAScript 5
"sourceType": "module" // 设置为"script"(默认)或者"module"您的代码位于ECMAScript模块中
"ecmaFeatures": {
"globalReturn": true, // 允许return在全球范围内发表声明
"impliedStrict": true, // 启用全局严格模式(如果ecmaVersion是5或更高)
"jsx": true, // 启用JSX
}
},
// 扩展配置文件,主要用来使用第三方的eslint的完整配置
// 一般输出的是整个eslint配置,也可以是别的形式
"extends": [
"eslint:recommended", // eslint官方扩展
"plugin:react/recommended", // 插件类型的扩展,也可以直接在 plugins 属性中进行设置
"eslint-config-standard", // 第三方扩展,eslint-config-*
]
// 配置插件,使用第三方的eslint的rules,命名格式eslint-plugin-*
// 插件一般输出的是规则
"plugins": [
"plugin1", // eslint-plugin-前缀可以从插件名称被省略。
"eslint-plugin-plugin2"
],
// 禁用一组文件的配置文件中的规则
"overrides": [
{
"files": ["*-test.js","*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
],
// 配置规则
// 1. 可以使用配置注释或配置文件来修改项目使用的规则
// 2. 每个规则必须是以下的值
// "off"或者0 关闭规则
// "warn"或者1 将规则打开为警告(不影响退出代码)
// "error"或者2 将规则打开为错误(触发时退出代码为1)
"rules": {
"plugin1/rule1": "error", // 重新定义上面插件中的规则
"quotes": [
"error",
"single"
],
'no-unused-vars': 0, // 定义了变量,未使用。可能有全局变量,别人使用的
'global-require': 0, // 不能使用require
'comma-dangle': 0, // 结尾逗号
'no-console': 0, // 不能有console,警告
'no-param-reassign': 0, // 不允许函数参数重新赋值
'no-unused-expressions': 0, // 不允许 this.toast.finally && this.toast.finally();形式
'func-names': 0, // 不允许使用 const getList = function() {...},是个警告
'no-prototype-builtins': 0, // 不能使用prototype操作
'no-restricted-syntax': 0, // 禁止使用for in
'prefer-const': 0, // 如果没改变的话,必须是const
'no-undef': 0, // 使用了未定义的变量
'arrow-parens': 0, // 箭头函数用小括号括起来
'object-shorthand': 0, //强制对象字面量缩写语法、
'guard-for-in': 0, // for in循环要用if语句过滤
'import/no-dynamic-require': 0 // 不能使用动态require
// ...
}
};
社区规范
代码风格并不是一个强制性的标准,每个团队都有自己的最佳实践。但是维护一套规则太费精力,所以出现了一些开源的代码规范
Airbnb 规范
Airbnb 是其中一个最流行的 JavaScript 代码规范,它差不多包含所有 JavaScript 的角度。校验比较严格。
Airbnb 规范的eslint实现
- eslint-config-airbnb
- 包含 ECMAScript 6 + 以及 React 的 ESLint 代码规范。
- .eslintrc 加入 "extends": "airbnb"
- eslint-config-airbnb-base
- 包含 ECMAScript 6 + 代码规范。
- .eslintrc 加入
"extends": ["airbnb-base"]
Standard 规范
标准的 JavaScript 代码规范,自带 linter & 代码自动修正
- 无须配置。 史上最便捷的统一代码风格的方式,轻松拥有。
- 自动的代码格式化。 只需运行 standard --fix 从此和脏乱差的代码说再见
- 提前发现风格及程序问题
Standard 规范的eslint实现
- eslint-config-standard
- .eslintrc 加入
"extends": ["standard"]
- .eslintrc 加入
Prettier
Prettier 是一个代码格式化插件。它并不关心你的语法是否正确,只关心你的代码格式,比如是否使用单引号,语句结尾是否使用分号等等。但是有部分规则和Eslint的冲突。
Prettier 的eslint实现
- eslint-config-prettier
- .eslintrc 加入
"extends": ["prettier"]
- .eslintrc 加入
集成
@vue/cli集成ESLint
@vue/cli中预设了三种代码风格的校验,正好是我们上面主要对比的规范。不得不说vue官方对于不同口味的开发者还是很懂的。感兴趣的可以看下vue官方
ESLint + Airbnb
// .eslint.js
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/essential',
'@vue/airbnb',
],
parserOptions: {
parser: 'babel-eslint',
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
},
};
ESLint + Standard
// .eslint.js
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/essential',
'@vue/standard'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
ESLint + Prettier
// .eslint.js
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint"
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
}
};
webpack 集成 ESLint
虽然已经在项目中配置了ESlint,但是还没有和webpack关联起来,所以要安装插件让webpack可以自动的读取ESLint规则
npm install eslint-webpack-plugin --save-dev
// webpack.config.js
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [new ESLintPlugin(options)],
// ...
};
rollup 集成 ESLint
同上webpack的插件配置
npm i rollup-plugin-eslint -D
// rollup.config.js
import { rollup } from "rollup";
import { eslint } from "rollup-plugin-eslint";
export default {
input: "main.js",
plugins: [
eslint({
/* your options */
})
]
};
vscode 集成 ESLint
项目已经配置了
Eslint, 但是vscode并没有在开发阶段给出对应的提示(应该出现红色波浪线)。
安装eslint 提供的 vscode 插件
手动开启eslint(默认不开启)
在团队中如何使用
怎么确保团队成员使用统一的配置?
- 使用中心化配置,团队内部使用统一的
eslint-config-xxx eslint-config-xxx其实将本地的.eslintrc.*配置提取成公共配置,方便维护- 本地项目通过
extends使用
开发eslint 规则
使用ESLint 官方提供的 Yeoman 的模板(generator-eslint)快速搭建项目结构,具体的规则主要是操作
AST了
快速创建模板
// 安装模板
npm install -g yo generator-eslint
// 创建目录
mkdir eslint-plugin-checkhost
cd eslint-plugin-checkhost
// 创建模板
yo eslint:plugin // 生成开发插件的项目结构
yo eslint:rule // 生成具体开发的规则文件
最终的项目结构
使用 astexplorer
使用 astexplorer 在线分析
AST结构
开发自定义规则
检测 .vue 文件
<template></template>中是否包含指定的域名,并给出提示
/**
* @fileoverview 域名是否切换为jdl.cn
* @author tianlu21
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
function defineTemplateBodyVisitor(
context,
templateBodyVisitor,
scriptVisitor
) {
if (context.parserServices.defineTemplateBodyVisitor == null) {
const filename = context.getFilename()
if (path.extname(filename) === '.vue') {
context.report({
loc: { line: 1, column: 0 },
message:
'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.'
})
}
return {}
}
return context.parserServices.defineTemplateBodyVisitor(
templateBodyVisitor,
scriptVisitor
)
}
function searchText(childrens, context) {
if(childrens) {
for(let i = 0; i < childrens.length; i++) {
if(childrens[i].children) {
searchText(childrens[i].children, context)
} else {
let text = childrens[i].value
if(/jd\.com/.test(text)) {
context.report({
node: childrens[i],
message: '域名是否切换到jdl.cn,请确认'
})
}
}
}
}
}
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "域名是否切换为jdl.cn",
},
// or "code" or "whitespace"
fixable: null,
},
create: function(context) {
return defineTemplateBodyVisitor(context, {
"VElement[name='template']"(node) {
searchText(node.children, context)
}
})
}
};
单测
npx mocha .\tests\lib\rules\checkhost.js
使用
npm i -D @vtian/eslint-plugin-checkhost