WEB前端线上系统课2022最新王红元瓯江分类

#1

download:WEB前端线上系统课2022最新王红元

前言

在前端工程化的规范中有一项就是自动化,自动化当中就包括了代码标准自动化。完成代码标准自动化能够解放团队消费力,提供团队消费效率。既然代码标准自动化能够带来如此的益处,我们自然要好好理解它并且控制它。

如今简直一切的团队运用的代码检查工具都是以 ESLint 为代表的 Linter 和 Prettier 停止配套运用。经过这些工具停止代码自动化检查,让开发者能够完整聚焦业务开发,不用在代码整理上破费过多的心机。相对其他需求编译的言语能够在编译阶段停止代码检查不同, JavaScript 不具备先天编译流程,同时是一种动态、宽松类型的言语,所以容易在运转时暴露错误。而经过 ESLint 能够让开发者提早将更多的错误或不合理的写法暴露在开发阶段。

任何工具我们都只要了解它的原理才干真正地控制它,所以下面我们将从 ESLint 的原理分析开端吧。

编译的根本原理

ESLint 是基于静态语法剖析(AST)停止工作的,默许运用 JavaScript 编译器 来解析 JS 代码生成 AST。有了 AST ,我们就能够基于 AST 对代码停止检查和修正。AST 是: Abstract Syntax Tree 的首字母缩写,中文叫做:笼统语法树。所谓笼统语法树指的是用来描绘被编译的代码的一个对象。

在这里我们需求简单理解一下编译技术的根本原理。所谓编译技术就是经过编译器将源代码转换成目的代码,这个过程就叫编译,其中包含词法剖析、语法剖析、语义剖析、编译优化、目的代码生成等步骤。

第一阶段就是将源码经过词法剖析和语法剖析得到源码 AST,第二阶段就是将源码 AST 转换(transform)成目的代码 AST,最后依据目的代码 AST 生成目的代码。

而 ESLint 中只用到传统编译流程的第一阶段,而第二阶段 ESLint 则有本人的完成逻辑。

ESLint 中主要触及到的 AST 学问点

词法剖析、语法剖析、语义剖析这一阶段的工作是编译器 来做的,这一局部本文不会作过多剖析,我们只需晓得经过编译器解析源代码便生成了 AST。我们需求做的工作简单来说就是下面的代码完成:

const fs = require('fs')
const path = require('path')
// 先要装置 npm install espree 包
const espree = require('espree')
const filePath = path.resolve('./test.js')
// 留意是经过 utf8 格式读取文件内容
const text = fs.readFileSync(filePath, "utf8")
// 编译成 AST
const ast = espree.parse(text,{ 
    comment: true, // 创立包含一切注释的顶级注释数组
    ecmaVersion: 6, // JS 版本
    // 指定其他言语功用,
    ecmaFeatures: { 
        jsx: true, // 启用JSX解析
        globalReturn: true // 在全局范围内启用return(当sourceType为“commonjs”时自动设置为true)
    }, 
    loc: true, // 将行/列位置信息附加到每个节点
    range: true, // 将范围信息附加到每个节点
    tokens: true // 创立包含一切标志的顶级标志数组
})
复制代码

经过上述代码我们就能够将一个 JavaScript 文件内容转换成 AST。

我们设置 test.js 的内容为:

var a = "稀土"
var b = "掘金"
复制代码

运用 fs.readFileSync 经过 utf8 字符集读取内容:

const filePath = path.resolve('./test.js')
const text = fs.readFileSync(filePath, "utf8")
复制代码

读取到的内容为:

var a = "稀土"\r\nvar b = "掘金"

我们能够看到源文件的内容是两行,而运用 fs.readFileSync 经过 utf8 字符集读取到的内容则变成了一行,换行则被交换成 \r\n 。我们的编译器正是依据这些特性停止设置 AST 数据的。

这个实例对象提供了十分多办法:

  • getText(node) - 返回给定节点的源码。省略 node ,返回整个源码。
  • getAllComments() - 返回一个包含源中一切注释的数组。
  • getCommentsBefore(nodeOrToken) - 返回一个在给定的节点或 token 之前的注释的数组。
  • getCommentsAfter(nodeOrToken) - 返回一个在给定的节点或 token 之后的注释的数组。
  • getCommentsInside(node) - 返回一个在给定的节点内的注释的数组。
  • getJSDocComment(node) - 返回给定节点的 JSDoc 注释,假如没有则返回 null。
  • isSpaceBetweenTokens(first, second) - 假如两个记号之间有空白,返回 true
  • getFirstToken(node, skipOptions) - 返回代表给定节点的第一个token

ESLint 中的访问者形式

我们从前文晓得源码经过编译器解析成 AST 之后,就是停止编译优化(transform),再停止转换成目的源码。而在 ESLint 中在取得源码的 AST 之后主要的工作就遍历 AST,然后找到需求 AST 的节点获取相关信息。详细 ESLint 中则运用了设计形式的访问者形式。所谓访问者形式就是当被操作的对象构造比拟稳定时,就能够把数据构造和作用于构造上的操作解耦合,使得操作汇合可相对自在地演化。

AST 中有很多类型的数据节点,比方 Literal 字面量Identifer 标识符VariableDeclarator 变量声明FunctionDeclaration 函数声明 ,那么我们需求这些节点数据停止操作的时分,就能够依据不同的类型创立不同的访问者,在遍历 AST 的时分,当命中对应访问者的时分,就调用对应访问者操作逻辑对节点停止操作。

那么要遍历 AST,就需求把 AST 组装成数组方式。ESLint 中是把 AST 数据停止铺平,把一切的节点组装成一个一维数组。详细就是经过 Traverser 类的 traverse 办法:

const nodeQueue = [];
Traverser.traverse(sourceCode.ast, {
    enter(node, parent) {
        node.parent = parent;
        nodeQueue.push({ isEntering: true, node });
    },
    leave(node) {
        nodeQueue.push({ isEntering: false, node });
    },
    visitorKeys: sourceCode.visitorKeys
});
复制代码

traverse 办法的完成原理也很简单,就是一个递归,在递归开端的时分调用 enter 办法,在递归完毕的时分调用 leave 办法,这样就每个节点的数据都构成了进入和退出的标志。这样在封装操作节点数据的访问者逻辑的时期就能够设置是在进入时停止处置,还是在退出的时分停止处置。

ESLint 的灵魂——规则

ESLint 中操作节点数据的访问者逻辑其实就在 ESLint 中的规则停止设置的。ESLint 的灵魂就是,每条规则都是独立且插件化的。下面就是一条 ESLint 的规则,主要功用就是检测代码中假如有运用到 var 关键字停止声明变量的,则停止提示和修复,详细修复就是把 var 关键字变成 let 关键字。

module.exports = {
    create(context) {
        const sourceCode = context.getSourceCode()
        return {
            VariableDeclaration(node) {
                if(node.kind === 'var') {
                    context.report({
                        node,
                        message:'不能用var',
                        fix(fixer) {
                            const varToken = sourceCode.getFirstToken(node)
                            return fixer.replaceText(varToken, 'let')
                        }
                    })
                }
            }
        };
    },
};
复制代码

上述代码中的 VariableDeclaration 函数就是一个访问者,它对应要操作的 AST 数据则是 VariableDeclarator 变量声明 类型的节点,有由于变量声明有 const、let、var 所以又进一步判别是不是 var。