Eslint在过往接触过的很多开源项目内都有它的身影,习惯一个人写代码了,总觉得它可有可无,但是归根结底,好处还是很多的。
- 可强制规范团队编码规范,让新旧组员编码习惯得到一致提升
- 可灵活定制团队编码风格,让预设规则符合新旧组员心理预期
- 增加项目代码的可维护性和可接入性,让新组员能快速适应项目的架构与需求
- 保障项目整体质量,可减少无用代码、重复代码、错误代码和漏洞代码的产生几率
什么是Eslint?
一个用来识别 ECMAScript/JavaScript 并且按照规则给出报告的代码检测工具(Stylelint、Eslint、Prettier)
中文官网:https://cn.eslint.org/
使用
ESLint 使用 AST 去分析代码中的模式,AST(Abstract Syntax Tree - 抽象语法树)
1.关于AST
参考:https://zhuanlan.zhihu.com/p/359456770
AST
工作流程:
- parse:把代码解析为AST。
- transform:对AST中的各个节点做相关操作,如新增、删除、替换、追加。业务开发 95%的代码都在这里。
- generator:把AST转换为代码。
// 三剑客
/*将源代码转为 AST*/
const parser = require('@babel/parser').parse;
/*AST 开发的核心,95% 以上的代码量都是通过 @babel/traverse 在写 visitor。*/
const traverse = require('@babel/traverse').default;
/*AST转为源代码*/
const generate = require('@babel/generator').default;
// 配套包
const types = require('@babel/types');
// 模板包
const template = require('@babel/template').default;
2.eslint安装
代码语言:javascript复制# 安装eslint
npm install eslint --save-dev
# 初始化配置,eslint同时可作为命令行工具使用
./node_modules/.bin/eslint --init
# window如果无法运行上述命令,可尝试
"node_modules/.bin/eslint" --init
要求
Node.js (>=6.14), npm version 3 。
3.使用eslint
大多数IDE都自带eslint插件,在编写代码的时候会按照eslint的规则进行代码提示。
4. vite集成eslint
安装插件vite-plugin-eslint(https://www.npmjs.com/package/vite-plugin-eslint),配置如下:
代码语言:javascript复制import { defineConfig } from 'vite'
import eslint from 'vite-plugin-eslint'
export default defineConfig({
plugins: [
eslint({
include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
exclude: ['node_modules'],
cache:false //开启缓存,减少检测时间(偶尔导致检测不触发),
fix:true,//自动修复源代码
})
]
})
Eslint配置
1.配置文件
ESLint 支持以下几种格式的配置文件,如果同一目录下 .eslintrc 和 package.json 同时存在,.eslintrc 优先级高会被使用,package.json 文件将不会被使用:
- JavaScript - 使用 .eslintrc.js 然后输出一个配置对象。
- YAML - 使用 .eslintrc.yaml 或 .eslintrc.yml 去定义配置的结构。
- JSON - 使用 .eslintrc.json 去定义配置的结构,ESLint 的 JSON 文件允许 JavaScript 风格的注释。
- (弃用) - 使用 .eslintrc,可以使 JSON 也可以是 YAML。
- package.json - 在 package.json 里创建一个 eslintConfig属性,在那里定义你的配置。
如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下:
- .eslintrc.js
- .eslintrc.yaml
- .eslintrc.yml
- .eslintrc.json
- .eslintrc
- package.json
ESLint 还支持层叠配置,层叠配置使用离要检测的文件最近的 .eslintrc文件作为最高优先级,然后才是父目录里的配置文件:
代码语言:javascript复制your-project
├── .eslintrc
├── lib
│ └── source.js
└─┬ tests
├── .eslintrc
└── test.js
默认情况下,ESLint 会在所有父级目录里寻找配置文件,一直到根目录,子目录内的配置规则优先级高于父目录,与父目录规则冲突时将覆盖父目录的规则。
需要将 ESLint 限制到一个特定的项目、目录时,可以在项目根目录下的 package.json 文件或者 .eslintrc.* 文件里的 eslintConfig 字段下设置 "root": true。ESLint 一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。
代码语言:javascript复制{
"root": true
}
2.行内配置
- /*eslint-disable*/ 和 /*eslint-enable*/,禁用启用规则
- /*global*/,定义全局变量
- /*eslint*/,配置规则
- /*eslint-env*/,指定当前运行环境
/* eslint-env node, mocha */
// 指定运行环境
alert(‘foo’);
/* eslint eqeqeq: 0, curly: 2 */
// 配置规则
alert(‘foo’);
/* global var1:writable, var2:writable */
// 指定全局变量
alert(‘foo’);
// 在整个文件中取消eslint检查:
/* eslint-disable */
alert(‘foo’);
// 在整个文件中禁用某一项eslint规则的检查:
/* eslint-disable no-alert */
alert(‘foo’);
// 在整个文件中禁用多项eslint规则的检查:
/* eslint-disable no-alert, no-console */
alert(‘foo’);
console.log(‘bar’);
// 针对某一行禁用eslint检查:
alert(‘foo’); // eslint-disable-line
// eslint-disable-next-line
alert(‘foo’);
// 针对某一行的某一具体规则禁用eslint检查:
alert(‘foo’); // eslint-disable-line no-alert
// eslint-disable-next-line no-alert
alert(‘foo’);
// 6. 针对某一行禁用多项具体规则的检查:
alert(‘foo’); // eslint-disable-line no-alert, quotes, semi
// eslint-disable-next-line no-alert, quotes, semi
alert(‘foo’);
[succes].vue文件直接写在script块内就行[/success]
3.配置项
3.1 root
root(布尔值),指定是否停止在父级目录寻找配置文件 。
3.2 env
env用于启用特定环境的全局变量
代码语言:javascript复制/** 启用特定环境的全局变量 */
env: {
es6: true /** 启用除了modules以外的所有 EC6 特性,开启后会自动设置ecmaVersion为6 */,
node: true /** Node.js 全局变量和 Node.js 作用域 */,
browser: true /** 浏览器全局变量 */,
commonjs: true /** CommonJS 全局变量和 CommonJS 作用域 (用于 Browserify/WebPack 打包的只在浏览器中运行的代码) */,
jest: false /** Jest 全局变量 */,
jquery: false /** jQuery 全局变量 */,
'shared-node-browser': false /** Node.js 和 Browser 通用全局变量 */,
worker: false /** Web Workers 全局变量 */,
amd: false /** 将 require() 和 define() 定义为像 amd 一样的全局变量 */,
mocha: false /** 添加所有的 Mocha 测试全局变量 */,
jasmine: false /** 添加所有的 Jasmine 版本 1.3 和 2.0 的测试全局变量。 */,
phantomjs: false /** phantomjs 全局变量 */,
protractor: false /** protractor 全局变量 */,
qunit: false /** QUnit 全局变量 */,
prototypejs: false /** Prototype.js 全局变量 */,
shelljs: false /** ShellJS 全局变量 */,
meteor: false /** Meteor 全局变量 */,
mongo: false /** MongoDB 全局变量 */,
applescript: false /** AppleScript 全局变量 */,
nashorn: false /** Java 8 Nashorn 全局变量 */,
serviceworker: false /** Service Worker 全局变量 */,
atomtest: false /** Atom 测试全局变量 */,
embertest: false /** Ember 测试全局变量 */,
webextensions: false /** WebExtensions 全局变量 */,
greasemonkey: false /** GreaseMonkey 全局变量 */,
}
3.3 parserOptions
parserOptions用于设置解析器相关配置,可用选项如下:
- ecmaVersion - 默认设置为 3,5(默认), 你可以使用 6、7、8、9 或 10 来指定你想要使用的 ECMAScript 版本。你也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)或 2018(同 9)或 2019 (same as 10)
- sourceType - 设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
- ecmaFeatures - 这是个对象,表示你想使用的额外的语言特性:
- globalReturn - 允许在全局作用域下使用 return 语句
- impliedStrict - 启用全局 strict mode (如果 ecmaVersion 是 5 或更高)
- jsx - 启用 JSX(支持 JSX 语法并不等同于支持 React。React 对 ESLint 无法识别的JSX语法应用特定的语义。如果你正在使用 React 并且想要 React 语义支持,需使用 eslint-plugin-react。)
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"semi": "error"
}
}
3.4 parser
parser用于设置接解析器,ESLint 默认使用Espree作为其解析器,你可以在配置文件中指定一个不同的解析器,只要该解析器符合下列要求:
- 它必须是一个 Node 模块,可以从它出现的配置文件中加载。通常,这意味着应该使用 npm 单独安装解析器包。
- 它必须符合 parser interface。
以下解析器与 ESLint 兼容:
- Esprima
- Babel-ESLint - 一个对Babel解析器的包装,使其能够与 ESLint 兼容。
- @typescript-eslint/parser - 将 TypeScript 转换成与 estree 兼容的形式,以便在ESLint中使用。
注意,在使用自定义解析器时,为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作,配置属性 parserOptions
仍然是必须的。解析器会被传入 parserOptions
,但是不一定会使用它们来决定功能特性的开关。
3.5 processor
processor用于指定处理器,插件可以提供处理器。处理器可以从另一种文件中提取 JavaScript 代码,然后让 ESLint 检测 JavaScript 代码。或者处理器可以在预处理中转换 JavaScript 代码。使用时键值由插件名和处理器名组成的串接字符串加上斜杠 组成
代码语言:javascript复制{
"plugins": ["a-plugin"],
"processor": "a-plugin/a-processor"
}
为特定类型的文件指定处理器,可使用 overrides 键和 processor 的组合。
代码语言:javascript复制{
"plugins": ["a-plugin"],
"overrides": [
{
"files": ["*.md"],
"processor": "a-plugin/markdown"
}
]
}
3.6 globals
当访问当前文件内未定义的变量时,no-undef 规则将发出警告。如果想在一个源文件里使用全局变量,可以 ESLint 中定义这些全局变量。
代码语言:javascript复制{
"globals": {
"var1": "writable",
"var2": "readonly"
}
}
对应的值设置为 "writable" 以允许重写变量,或 "readonly" 不允许重写变量。
3.7 plugins
plugins 关用来引入指定的插件。插件名称可以省略 eslint-plugin- 前缀 。
注意
插件是相对于 ESLint 进程的当前工作目录解析的。换句话说,ESLint 将加载与用户通过从项目 Node 交互解释器运行 ('eslint-plugin-pluginname') 获得的相同的插件。
参考文档:https://cn.eslint.org/docs/developer-guide/working-with-plugins
每个插件是一个命名格式为 eslint-plugin- 的 npm 模块,比如 eslint-plugin-jquery。
在 ESLint 中,插件可以暴露额外的规则以供使用。为此,插件必须输出一个 rules对象,包含规则 ID 和对应规则的一个键值对。
代码语言:javascript复制module.exports = {
rules: {
"dollar-sign": {
create: function (context) {
// rule implementation ...
}
}
}
};
3.8 rules
rules用于配置规则,ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,必须将规则 ID 设置为下列值之一:
- "off" 或 0 - 关闭规则
- "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
- "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
{
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"]
}
}
配置定义在插件中的一个规则的时候,你必须使用 插件名/规则ID 的形式:
代码语言:javascript复制{
"plugins": [
"plugin1"
],
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"],
"plugin1/rule1": "error"
}
}
为一组文件定义规则,可以使用overrides和 rules:
代码语言:javascript复制{
"rules": {...},
"overrides": [
{
"files": ["*-test.js","*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
]
}
3.9 settings
ESLint 支持在配置文件添加共享设置。可以添加 settings 对象到配置文件,它将提供给每一个将被执行的规则。如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置。
代码语言:javascript复制{
"settings": {
"sharedData": "Hello"
}
}
3.10 extends
可共享的配置:https://cn.eslint.org/docs/developer-guide/shareable-configs
extends用于继承共享的配置规则,可共享的配置 是一个 npm 包,它输出一个配置对象。要确保这个包安装在 ESLint 能请求到的目录下(extends 属性值可以省略包名的前缀 eslint-config-。
)。
extends 属性值可以由组成:plugin:包名 (可以是省略了前缀的插件名字也可以是完整包名,比如,react)/配置名称 (比如 recommended) ,也可以是一个省略 eslint-config-的共享配置。
代码语言:javascript复制extends: [
// Airbnb JavaScript Style Guide https://github.com/airbnb/javascript
'airbnb-base',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
],
在配置文件中,使用 "extends": "eslint:recommended" 来启用推荐的规则。
常用的规则列表:https://cn.eslint.org/docs/rules/
代码语言:javascript复制/** 扩展 */
extends: ['eslint:recommended' /** eslint:recommended(自动启用部分列核心功能) 、 eslint:all(启用当前Eslint全部核心功能) */],
Plugins和Extends的区别
简单的说Plugin是声明了一堆规则,使用需要自己在rules中进行自定义。extends(集成),同时具有plugin导入的能力,还引入了自带的规则风格。(Extends看以看作是当前配置对象的功能完备的子集)
.eslintignore
可以通过在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录。.eslintignore 文件是一个纯文本文件,其中的每一行都是一个 glob 模式表明哪些路径应该忽略检测。
当 ESLint 运行时,在确定哪些文件要检测之前,它会在当前工作目录中查找一个 .eslintignore 文件。如果发现了这个文件,当遍历目录时,将会应用这些默认设置。一次只有一个 .eslintignore 文件会被使用,所以,不是当前工作目录下的 .eslintignore 文件将不会被用到。
Globs 匹配使用 node-ignore,所以大量可用的特性有:
- 以 # 开头的行被当作注释,不影响忽略模式。
- 路径是相对于 .eslintignore 的位置或当前工作目录。通过 --ignore-pattern command 传递的路径也是如此。
- 忽略模式同 .gitignore 规范
- 以 ! 开头的行是否定模式,它将会重新包含一个之前被忽略的模式。
- 忽略模式依照 .gitignore 规范.
重要
注意代码库的 node_modules 目录,比如,一个 packages 目录,默认情况下不会被忽略,需要手动添加到 .eslintignore。
命令行
代码语言:javascript复制Fixing problems:
--fix Automatically fix problems
--fix-dry-run Automatically fix problems without saving the changes to the file system
--fix-type Array Specify the types of fixes to apply (problem, suggestion, layout)
提示
大多数IDE都可以配置保存时自动Fix
常用插件
1.Airbnb
检查js常用的一些语法错误。
NPM:https://www.npmjs.com/package/eslint-config-airbnb-base
Github:https://github.com/airbnb/javascript
2.@typescript-eslint/eslint-plugin
typescript语法检测支持。
Npm:https://www.npmjs.com/package/@typescript-eslint/eslint-plugin
3.eslint-plugin-import
添加对es6 import语法的检测支持。
Npm:https://www.npmjs.com/package/eslint-plugin-import
4.eslint-plugin-vue
Vue语法检查,使用时必须替换默认的 eslint解析器为vue-eslint-parser,因为Vue.js 单文件组件不是纯 JavaScript,而是自定义文件格式。
官方网站:https://eslint.vuejs.org/
Npm:https://www.npmjs.com/package/eslint-plugin-vue
代码语言:javascript复制module.exports = {
extends: [
// add more generic rulesets here, such as:
// 'eslint:recommended',
'plugin:vue/vue3-recommended',
// 'plugin:vue/recommended' // Use this if you are using Vue.js 2.x.
],
rules: {
// override/add rules settings here, such as:
// 'vue/no-unused-vars': 'error'
}
}
5.eslint-import-resolver-typescript
让Typescript和eslint-plugin-import一起正常工作。
https://www.npmjs.com/package/eslint-import-resolver-typescript
6.eslint-import-resolver-alias
支持通过别名import、支持import自定义类型的文件
https://www.npmjs.com/package/eslint-import-resolver-alias
代码语言:javascript复制{
settings: {
'import/resolver': {
typescript: {
project: path.resolve(__dirname, './tsconfig.json'),
},
},
}
}
7.eslint-plugin-prettier
prettier 官方提供的 ESLint 插件 eslint-plugin-prettier,将 prettier 作为 ESLint 的规则来使用,代码不符合 Prettier 的标准时,会报一个 ESLint 错误,同时也可以通过 eslint --fix 来进行格式化。
Prettier:https://prettier.io/docs/en/options.html
https://www.npmjs.com/package/eslint-plugin-prettier
使用的同时需要安装prettier,插件遵循prettier的配置
参考文档
https://prettier.io/docs/en/integrating-with-linters.html#docsNav
8.eslint-config-prettier
关闭所有与prettier有冲突的规则。
https://www.npmjs.com/package/eslint-config-prettier
代码语言:javascript复制extends:{
'plugin:prettier/recommended',
}
//安装这个插件后上方等价于
{
"extends": [
"prettier"
],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}
规则解释
1.no-prototype-builtins
在 JS 中,往往通过改变原型链实现继承。一旦原型链发生改变,原先可以访问到的原型属性、方法便可能无法访问。
考虑最极端的情况,若 obj 原先原型链的最顶端是 Object,此时可以通过原型链访问 Object.hasOwnProperty 方法;而若改变后,顶端不再是 Object,那么访问 obj.hasOwnProperty 访问就会得到 undefined。因此,直接从对象访问原型方法,很可能会带来隐藏的 BUG。
代码语言:javascript复制Object.prototype.hasOwnProperty.call(obj, 'key')
采用以上方法,即直接在 Object 对象上调用其方法,利用 call 改变其 this 指向到我们的目标对象上,即可安全使用 hasOwnProperty 方法了。