ESLint 是如何使用和实现的?

简介: 前言今天这篇文章,主要聊聊什么是ESLint,为什么要用它?它的实现原理是什么?工作中如何使用的ESLint,以及如何自定义ESLint规则。本文整理自以下文章:• 掘金:eslint工作原理探讨• 手摸手教你写eslint插件• 慕课网:《大前端》第七周「团队协作」

前言



今天这篇文章,主要聊聊什么是ESLint,为什么要用它?它的实现原理是什么?工作中如何使用的ESLint,以及如何自定义ESLint规则。


本文整理自以下文章:




  • 慕课网:《大前端》第七周「团队协作」


什么是ESLint & 为什么使用它



为什么要使用ESLint?


JavaScript是一个动态的弱类型语言,在代码编写的过程中,经常会出错,而因为其没有编译程序,为了寻找代码错误的地方,需要在执行的过程中不断的调试。


ESLint的作用就是让你在开发过程中发现自己的代码问题以及不规范的地方,提前发现问题所在,并且可以规范团队的代码风格保持一致。


什么是ESLint?


ESlint是一个开源的JS代码检查工具,它的目标是提供一个插件化的JavaScript代码检测工具。


ESLint 的核心就是其中包含的各种规则,这些规则大多为众多开发者经验的结晶:








总的来说,ESLint是一套每一个人都应该了解的并且遵循的JS代码规范。它可以让我们的代码风格一致、更加健壮、减少错误并用上社区的最佳实践。


原理



在许多方面,它和 JSLint、JSHint 相似,除了少数的例外:


  • ESLint 使用 Espree 解析 JavaScript。


  • ESLint 使用 AST 去分析代码中的模式


  • ESLint 是完全插件化的。每一个规则都是一个插件并且你可以在运行时添加更多的规则。


AST是Abstract Syntax Tree(抽象语法树)的缩写,如下图。


网络异常,图片无法展示
|


AST


也就是说,eslint使用Espress把js语法转换成AST。然后通过 AST selectors找到静态代码中的内容,再根据rule的规则去判断这一段js是否符合eslint的规范。


安装和初始化ESlint


新建一个空的文件夹,执行以下的命令:


1、 npm init -y


2、 npm install eslint -D


3、 npx eslint --init


网络异常,图片无法展示
|


image-20200112155041929


完成以上的步骤,我们将会得到以下的文件夹:


网络异常,图片无法展示
|


image-20200112155112994


rule是如何工作的?


ESLint 的核心就是规则(rule),每条规则都是独立的,且都可以被设置为禁止off,警告warn,或者报错error


我们选择"no-debugger": "error" 来看看 rule 是如何工作的。源码如下:

module.exports = {
    meta: {
        type: "problem",
        docs: {
            description: "disallow the use of `debugger`",
            category: "Possible Errors",
            recommended: true,
            url: "https://eslint.org/docs/rules/no-debugger"
        },
        fixable: null,
        schema: [],
        messages: {
            unexpected: "Unexpected 'debugger' statement."
        }
    },
    create(context) {
        return {
            DebuggerStatement(node) {
                context.report({
                    node,
                    messageId: "unexpected"
                });
            }
        };
    }
};


一条 rule 就是一个 node 模块,其主要由 metacreate 两部分组成,其中


  • meta 代表了这条规则的元数据,如其类别,文档,可接收的参数的 schema 等等。


  • create:如果说 meta 表达了我们想做什么,那么 create 则表达了这条 rule 具体会怎么分析代码;


Create 返回的是一个对象,其中最常见的键的名字可以是上面我们提到的选择器,在该选择器中,我们可以获取对应选中的内容,随后我们可以针对选中的内容作一定的判断,看是否满足我们的规则,如果不满足,可用 context.report()抛出问题,ESLint 会利用我们的配置对抛出的内容做不同的展示。


上面的代码实际上表明在匹配到 debugger 语句时,会抛出 “Unexpected ‘debugger’ statement.” 。


网络异常,图片无法展示
|


AST


关于更多的Rules规则,可以查看「eslint工作原理探讨」


plugin的概念


plugin 有两重概念:


  1. 一是 ESLint 配置项中的字段,如 plugins: ['react'];


  1. 二是社区封装的 ESLint plugin,在 npm 上搜索eslint-plugin-就能发现很多,比较出名的有 eslint-plugin-reacteslint-plugin-import


plugin 其实可以看作是第三方规则的集合,ESLint 本身规则只会去支持标准的 ECMAScript语法,但是如果我们想在 React 中也使用 ESLint 则需要自己去定义一些规则,这就有了 eslint-plugin-react


我们在日常的工作中,也可以自定义符合自己团队风格的plugin提供给其他的队友使用。


工作中是如何使用ESLint的?


通常我们再日程的工作中,不会使用npx eslint执行代码检查,而是在IDE中自动提醒Eslint的错误。


在Vscode中,需要安装ESLint插件。


网络异常,图片无法展示
|


image-20200112161825873


如果使用该插件,需要在项目中或者全局使用npm install eslint安装eslint,否则,ESLint插件会报如下错误。


网络异常,图片无法展示
|


image-20200112162052045


当然,如果你用的webstorm,就不用这么麻烦安装插件啦。


VsCode中可以使用自动保存autoSave, ctrl + P,使用保存时自动格式化ESLint


网络异常,图片无法展示
|


image-20200112163411079


这里有一份课程中提供的settings配置,可以供小伙伴们添加到settings.json中使用:


...
  "eslint.validate": [
    "javascriptreact",
    "typescriptreact",
    {
      "language": "html",
      "autoFix": true
    },
    {
      "language": "vue",
      "autoFix": true
    },
    {
      "language": "javascript",
      "autoFix": true
    },
    {
      "language": "typescript",
      "autoFix": true
    }
  ],
...
  "editor.codeActionsOnSave": {
    "source.fixAll.tslint": true,
    "source.fixAll.eslint": true
  },
  // prettier
  "prettier.trailingComma": "es5",
  // vetur
  "vetur.format.defaultFormatter.js": "none",
  "vetur.validation.template": false,
  // default use eslint, NO NEED re-config for twice
  // "vetur.format.defaultFormatterOptions": {
  //   "prettier": {
  //     "semi": false,
  //     "singleQuote": true,
  //     "eslintIntegration": true,
  //     "trailingComma": "all"
  //   }
  // },
  // Files exclude from tree


自定义的ESLint规则


只需要满足 ESLint 的规定,ESLint 支持自定义 parser,实际上社区在这方面也做了很多工作。

比如


  • babel-eslint,A wrapper for Babel’s parser used for ESLint



自定义的 parser 使用方法如下:

{
    "parser": "./path/to/awesome-custom-parser.js"
}


通过自定义 parser ,ESLint 的使用场景又被大大拓展。


下面,我们结合一个小例子,看看自定义的规则是如何实现的:


插件目标:禁止项目中setTimeout的第二个参数是数字。


实现步骤如下


1、安装NPM包


ESLint官方为了方便开发者开发插件,提供了使用Yeoman模板(generator-eslint)。

对于Yeoman我们只需知道它是一个脚手架工具,用于生成包含指定框架结构的工程化目录结构。

npm install -g yo generator-eslint


创建一个文件夹:

mkdir eslint-plugin-demo
cd eslint-plugin-demo


命令行初始化ESLint插件的项目结构:

yo eslint:plugin


下面进入命令行交互流程,流程结束后生成ESLint插件项目框架和文件。

? What is your name? OBKoro1
? What is the plugin ID? korolint   // 这个插件的ID是什么
? Type a short description of this plugin: XX公司的定制ESLint rule // 输入这个插件的描述
? Does this plugin contain custom ESLint rules? Yes // 这个插件包含自定义ESLint规则吗?
? Does this plugin contain one or more processors? No // 这个插件包含一个或多个处理器吗
// 处理器用于处理js以外的文件 比如.vue文件
   create package.json
   create lib/index.js
   create README.md


现在可以看到在文件夹内生成了一些文件夹和文件,但我们还需要创建规则具体细节的文件。


2、创建规则


上一个命令行生成的是ESLint插件的项目模板,这个命令行是生成ESLint插件具体规则的文件。

yo eslint:rule // 生成 eslint rule的模板文件


创建规则命令行交互:

? What is your name? OBKoro1
? Where will this rule be published? (Use arrow keys) // 这个规则将在哪里发布?
❯ ESLint Core  // 官方核心规则 (目前有200多个规则)
  ESLint Plugin  // 选择ESLint插件
? What is the rule ID? settimeout-no-number  // 规则的ID
? Type a short description of this rule: setTimeout 第二个参数禁止是数字  // 输入该规则的描述
? Type a short example of the code that will fail:  占位  // 输入一个失败例子的代码
   create docs/rules/settimeout-no-number.md
   create lib/rules/settimeout-no-number.js
   create tests/lib/rules/settimeout-no-number.js


加了具体规则文件的项目结构

.
├── README.md
├── docs // 使用文档
│   └── rules // 所有规则的文档
│       └── settimeout-no-number.md // 具体规则文档
├── lib // eslint 规则开发
│   ├── index.js 引入+导出rules文件夹的规则
│   └── rules // 此目录下可以构建多个规则
│       └── settimeout-no-number.js // 规则细节
├── package.json
└── tests // 单元测试
    └── lib
        └── rules
            └── settimeout-no-number.js // 测试该规则的文件


3、安装项目依赖

npm install


rule完整文件


lib/rules/settimeout-no-number.js:

module.exports = {
    meta: {
        docs: {
            description: "setTimeout 第二个参数禁止是数字",
        },
        fixable: null,  // 修复函数
    },
    // rule 核心
    create: function (context) {
        // 公共变量和函数应该在此定义
        return {
            // 返回事件钩子
            'CallExpression': (node) => {
                if (node.callee.name !== 'setTimeout') return // 不是定时器即过滤
                const timeNode = node.arguments && node.arguments[1] // 获取第二个参数
                if (!timeNode) return // 没有第二个参数
                // 检测报错第二个参数是数字 报错
                if (timeNode.type === 'Literal' && typeof timeNode.value === 'number') {
                    context.report({
                        node,
                        message: 'setTimeout第二个参数禁止是数字'
                    })
                }
            }
        };
    }
};


context.report():这个方法是用来通知ESLint这段代码是警告或错误的,用法如上。在这里查看contextcontext.report()的文档。


规则写完了,原理就是依据AST解析的结果,做针对性的检测,过滤出我们要选中的代码,然后对代码的值进行逻辑判断


4、发布插件


eslint插件都是以npm包的形式来引用的,所以需要把插件发布一下:


  1. 注册:如果你还未注册npm账号的话,需要去注册一下。


  1. 登录npm: npm login


  1. 发布npm包: npm publish即可,ESLint已经把package.json弄好了。


5、集成到项目:


安装npm包:npm i eslint-plugin-korolint -D


常规的方法: 引入插件一条条写入规则

// .eslintrc.js
module.exports = {
  plugins: [ 'korolint' ],
  rules: { 
    "korolint/settimeout-no-number": "error"
 }
}


extends继承插件配置:


当规则比较多的时候,用户一条条去写,未免也太麻烦了,所以ESLint可以继承插件的配置


修改一下lib/rules/index.js文件:

'use strict';
var requireIndex = require('requireindex');
const output = {
  rules: requireIndex(__dirname + '/rules'), // 导出所有规则
  configs: {
    // 导出自定义规则 在项目中直接引用
    koroRule: {
      plugins: ['korolint'], // 引入插件
      rules: {
        // 开启规则
        'korolint/settimeout-no-number': 'error'
      }
    }
  }
};
module.exports = output;


使用方法:


使用extends来继承插件的配置,extends不止这种继承方式,即使你传入一个npm包,一个文件的相对路径地址,eslint也能继承其中的配置。


// .eslintrc.js
module.exports = {
  extends: [ 'plugin:korolint/koroRule' ] // 继承插件导出的配置
}


总结



ESLint 可谓是现代前端开发过程中必备的工具了。ESLint 做为必备工具之一,能减少团队协作的问题,个人代码风格问题,减少Bug的错误,是非常值得我们深入了解学习的。

感谢掘金作者@OBKoro1,@zhangwang的分享。


更多参考文章





目录
相关文章
|
前端开发
万能的登录页面(含源码)
这篇文章提供了一个万能的登录页面样式和源码,页面简洁美观,作者还提供了页面样式的CSS代码,并且提到源码和图片可以在评论区获取。
万能的登录页面(含源码)
|
监控 应用服务中间件 Linux
轻松解决日志文件积压问题:掌握logrotate的技巧
轻松解决日志文件积压问题:掌握logrotate的技巧
1075 1
|
人工智能 运维 Cloud Native
把汉堡王搬上阿里云
把汉堡王搬上阿里云
386 9
|
Java 数据库连接 API
什么是 Hibernate?为什么使用 Hibernate?
【8月更文挑战第21天】
500 0
|
存储 人工智能 编解码
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
随着人工智能、高性能计算等领域的快速发展,GPU云服务器因其强大的计算能力和灵活的资源分配方式,成为越来越多企业和个人用户的首选。2024年,阿里云针对GPU云服务器推出了新的收费标准及活动,gn6v、gn7i、gn6i等实例的gpu云服务器有优惠,本文为大家介绍2024年,阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考。
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
|
消息中间件 Kafka
kafka配置中启动zookeeper时没有启动成功的解决办法
kafka配置中启动zookeeper时没有启动成功的解决办法
|
Linux 网络安全 数据安全/隐私保护
VsCode SSH远程设置不用重复输入密码
VsCode SSH远程设置不用重复输入密码
|
存储 NoSQL 关系型数据库
轻松打卡:使用Spring Boot和Redis Bitmap构建高效签到系统【redis实战 四】
轻松打卡:使用Spring Boot和Redis Bitmap构建高效签到系统【redis实战 四】
814 0
|
SQL 关系型数据库 MySQL
MySQL多表查询之子查询详解
在数据库查询中,多表查询是一项非常常见且重要的任务。它允许我们从多个相关联的表中检索和组合数据,以满足各种复杂的查询需求。在多表查询中,子查询是一种强大的工具,用于在查询中嵌套另一个查询。本文将深入探讨MySQL中的子查询,包括什么是子查询、如何编写子查询以及使用子查询解决的常见查询问题。
581 1
|
存储 Java Spring
SpringBoot 如何使用 @ControllerAdvice 注解处理异常消息
SpringBoot 如何使用 @ControllerAdvice 注解处理异常消息