babel学习

Posted on By yuanfang

npx

注意attention: 学习转载来源-npx是什么?

npx 会帮你执行依赖包里的二进制文件。举例来说,之前我们可能会写这样的命令:

npm i -D webpack 
./node_modules/.bin/webpack -v

如果你对 bash 比较熟,可能会写成这样:

npm i -D webpack
npm bin/webpack -v

有了 npx,你只需要这样:

npm i -D webpack
npx webpack -v

也就是说 npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装!
主要特点:
1、临时安装可执行依赖包,不用全局安装,不用担心长期的污染。
2、可以执行依赖包中的命令,安装完成自动运行。
3、自动加载node_modules中依赖包,不用指定$PATH。
4、可以指定node版本、命令的版本,解决了不同项目使用不同版本的命令的问题。

babel7

babel7入门

注意attention: 学习转载来源-babel7学习笔记

npm i --save-dev @babel/cli @babel/core
npm i --save-dev @babel/preset-env

npx babel src/index.js -d dist/ --presets=@babel/preset-env
//babel.config.js
module.exports = function (api) {
    api.cache(true);
    const presets = ['@babel/env'];
    const plugins = [['@babel/proposal-decorators', { legacy: true }]];

    return {
        presets,
        plugins
    };
}
//.babelrc
{
  "presets": [
    [
      "@babel/env",
      {
        "useBuiltIns": "usage", // 该配置项有三个属性: "usage", "entry", "false"。默认为false。 需要事先说明一下,这个属性如果是"usage"或"entry"时,必须要安装@babel/polyfill,因为在转换出来的代码中,会引入core-js下面的包。
        "targets": {
          "chrome": "64" // targets配置项用于指定需要支持的环境,对于前端开发来说,主要指的就是浏览器版本。(targets还可以指定node、android、ios等其他环境) 
        }
      }
    ]
  ],
  "plugins": [['@babel/proposal-decorators', { "legacy": true }]]
}

babel执行presets和plugins的顺序如下:

  1. Plugins先于Presets执行。
  2. Plugins由数组中的第一个plugin开始依次执行。
  3. Presets与Plugins执行顺序相反,由数组中最后一个preset开始执行。
{
  plugins: [
    // Stage 0
    "@babel/plugin-proposal-function-bind",

    // Stage 1
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    ["@babel/plugin-proposal-optional-chaining", { loose: false }],
    ["@babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
    ["@babel/plugin-proposal-nullish-coalescing-operator", { loose: false }],
    "@babel/plugin-proposal-do-expressions",

    // Stage 2
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", { loose: false }],
    "@babel/plugin-proposal-json-strings",
  ],
}

webpack babel-loader的配置

module: {
  rules: [
    {
      test: /\.js|jsx$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}

babel7插件

注意attention: 学习转载来源-babel入门到精通

  1. babel工作流
    输入字符串 -> @babel/parser parser -> AST -> transformer[s] -> AST -> @babel/generator -> 输出字符串

  2. Toolings

     @babel/parser 将源代码解析成 AST。
     @babel/generator 将AST解码生 js代码。
     @babel/core 包括了整个babel工作流,也就是说在@babel/core里面我们会使用到@babel/parser、transformer[s]、以及@babel/generator。
     @babel/code-frame 用于生成错误信息并且打印出错误原因和错误行数。(其实就是个console工具类)
     @babel/helpers 也是工具类,提供了一些内置的函数实现,主要用于babel插件的开发。
     @babel/runtime 也是工具类,但是是为了babel编译时提供一些基础工具库。作用于transformer[s]阶段,当然这是一个工具库,如果要使用这个工具库,还需要引入@babel/plugin-transform-runtime,它才是transformer[s]阶段里面的主角。
     @babel/template 也是工具类,主要用途是为parser提供模板引擎,更加快速的转化成AST
     @babel/traverse 也是工具类,主要用途是来便利AST树,也就是在@babel/generator过程中生效。
     @babel/types 也是工具类,主要用途是在创建AST的过程中判断各种语法的类型。
    

注意attention: 学习转载来源-快速写一个babel插件-https://www.jianshu.com/p/44c0075fd043

注意attention: AST explore-https://astexplorer.net/

注意attention: babel是如何读懂JS代码的

  • 分词:将整个代码字符串分割成 语法单元 数组

首先解释一下什么是语法单元:语法单元是被解析语法当中具备实际意义的最小单元,通俗点说就是类似于自然语言中的词语。
看这句话“2020年奥运会将在东京举行”,不论词性及主谓关系等,人第一步会把这句话拆分成:2020年、奥运会、将、在、东京、举行。这就是分词:把整句话拆分成有意义的最小颗粒,这些小块不能再被拆分,否则就失去它所能表达的意义了。
那么回到代码的解析当中,JS代码有哪些语法单元呢?大致有以下这些(其他语言也许类似但通常都有区别):

    空白:JS中连续的空格、换行、缩进等这些如果不在字符串里,就没有任何实际逻辑意义,所以把连续的空白符直接组合在一起作为一个语法单元。
    注释:行注释或块注释,虽然对于人类来说有意义,但是对于计算机来说知道这是个“注释”就行了,并不关心内容,所以直接作为一个不可再拆的语法单元
    字符串:对于机器而言,字符串的内容只是会参与计算或展示,里面再细分的内容也是没必要分析的
    数字:JS语言里就有16、10、8进制以及科学表达法等数字表达语法,数字也是个具备含义的最小单元
    标识符:没有被引号扩起来的连续字符,可包含字母、_、$、及数字(数字不能作为开头)。标识符可能代表一个变量,或者true、false这种内置常量、也可能是if、return、function这种关键字,是哪种语义,分词阶段并不在乎,只要正确切分就好了。
    运算符:+、-、*、/、>、<等等
    括号:(...)可能表示运算优先级、也可能表示函数调用,分词阶段并不关注是哪种语义,只把“(”或“)”当做一种基本语法单元
    还有其他:如中括号、大括号、分号、冒号、点等等不再一一列举
  • 语义分析:在分词结果的基础之上分析 语法单元之间的关系, 语义分析就是把词汇进行立体的组合,确定有多重意义的词语最终是什么意思、多个词语之间有什么关系以及又应该再哪里断句等。

在编程语言的解析中有两个很相似但是又有区别的重要概念:
1. 语句:语句是一个具备边界的代码区域,相邻的两个语句之间从语法上来讲互不干扰,调换顺序虽然可能会影响执行结果,但不会产生语法错误, 比如return true、var a = 10、if (…) {…}
2. 表达式:最终有个结果的一小段代码,它的特点是可以原样嵌入到另一个表达式; 比如myVar、1+1、str.replace(‘a’, ‘b’)、i < 10 && i > 0等.

语义分析的过程又是个遍历语法单元的过程,不过相比较而言更复杂,因为分词过程中,每个语法单元都是独立平铺的,而语法分析中,语句和表达式会以树状的结构互相包含。针对这种情况我们可以用栈,也可以用递归来实现。
我继续上面的例子给出语义分析的代码,代码很长(请看转载博客),先在最开头说明几个函数是做什么的:

    nextStatement:读取并返回下一个语句
    nextExpression:读取并返回下一个表达式
    nextToken:读取下一个语法单元(或称符号),赋值给curToken
    stash:暂存当前读取符号的位置,方便在需要的时候返回
    rewind:返回到上一个暂存点
    commit:上一个暂存点不再被需要,将其销毁
    这里stash、rewind、commit都跟读取位置暂存相关,什么样的情况会需要返回到暂存点呢?有时同一种语法单元有可能代表不同类型的表达式的开始。先stash,然后按照其中一种尝试解析,如果解析成功了,那么暂存点就没用了,commit将其销毁。如果解析失败了,就用rewind回到原来的位置再按照另一种方式尝试去解析。

babel7文档

注意attention: 学习文档来源中文文档-https://babel.docschina.org/docs/en/

学习文档来源官网-https://babeljs.io/docs/en/

  • stage

注意attention: babel-preset-stage–https://github.com/babel/babel/blob/master/packages/babel-preset-stage-0/README.md

import {parse} from '@babel/parser';
import generate from '@babel/generator';

const code = 'class Example {}';
const ast = parse(code);
const output = generate(ast, { /* options */ }, code);
  • @babel/traverse
import * as parser from "@babel/parser";
import traverse from "@babel/traverse";

const code = `function square(n) {
  return n * n;
}`;

const ast = parser.parse(code);

traverse(ast, {
  enter(path) {
    if (path.isIdentifier({ name: "n" })) {
      path.node.name = "x";
    }
  }
});

babel手册

babel-handbook(https://github.com/jamiebuilds/babel-handbook/tree/master/translations/zh-Hans)

ast-Acorn

注意attention: 转载来源-使用Acorn来解析 JavaScript

estree-https://github.com/estree/estree

    1. Identifier // 标识符,我觉得应该是这么叫的,就是我们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:  
        interface Identifier <: Expression, Pattern {
            type: "Identifier";
            name: string;
        }
    2. Literal // 字面量的值,我们可以看出字面量值的类型,字符串,布尔,数值,null 和正则
        interface Literal <: Expression {
            type: "Literal";
            value: string | boolean | null | number | RegExp;
        }
    3. RegExpLiteral // 正则字面量的,为了更好地来解析正则表达式的内容,添加多一个 regex 字段,里边会包括正则本身,以及正则的 flags。
        interface RegExpLiteral <: Literal {
            regex: {
                pattern: string;
                flags: string;
            };
        }
    4. Programs //一般这个是作为跟节点的,即代表了一棵完整的程序代码树
        interface Program <: Node {
            type: "Program";
            body: [ Statement ];
        }
    5. Functions // 函数声明或者函数表达式节点。id 是函数名,params 属性是一个数组,表示函数的参数。body 是一个块语句。
        interface Function <: Node {
            id: Identifier | null;
            params: [ Pattern ];
            body: BlockStatement;
        }
    6. Statement // 语句节点没什么特别的,它只是一个节点,一种区分,但是语句有很多种

        6.1 ExpressionStatement // 表达式语句节点,a = a + 1 或者 a++ 里边会有一个 expression 属性指向一个表达式节点对象
            interface ExpressionStatement <: Statement {
                type: "ExpressionStatement";
                expression: Expression;
            }
        6.2 BlockStatement // 块语句节点,举个例子:if (...) { // 这里是块语句的内容 },块里边可以包含多个其他的语句,所以有一个 body 属性,是一个数组,表示了块里边的多个语句
            interface BlockStatement <: Statement {
                type: "BlockStatement";
                body: [ Statement ];
            }
        6.3 EmptyStatement // 一个空的语句节点,没有执行任何有用的代码,例如一个单独的分号 ;
        6.4 DebuggerStatement //debugger,就是表示这个,没有其他了
        6.5 WithStatement // with 语句节点,里边有两个特别的属性,object 表示 with 要使用的那个对象(可以是一个表达式),body 则是对应 with 后边要执行的语句,一般会是一个块语句。
            interface WithStatement <: Statement {
                type: "WithStatement";
                object: Expression;
                body: Statement;
            }
        6.6 ReturnStatement // 返回语句节点,argument 属性是一个表达式,代表返回的内容。
                interface ReturnStatement <: Statement {
                    type: "ReturnStatement";
                    argument: Expression | null;
                }
        6.7 LabeledStatement // label 语句
                interface LabeledStatement <: Statement {
                    type: "LabeledStatement";
                    label: Identifier;
                    body: Statement;
                }
        6.8 BreakStatement // break 语句节点,会有一个 label 属性表示需要的 label 名称,当不需要 label 的时候(通常都不需要),便是 null。
                interface BreakStatement <: Statement {
                    type: "BreakStatement";
                    label: Identifier | null;
                }
        6.9 ContinueStatement 
                interface ContinueStatement <: Statement {
                    type: "ContinueStatement";
                    label: Identifier | null;
                }
        6.10 IfStatement // if 语句节点,很常见,会带有三个属性,test 属性表示 if (...) 括号中的表达式。consequent 属性是表示条件为 true 时的执行语句,通常会是一个块语句。alternate 属性则是用来表示 else 后跟随的语句节点,通常也会是块语句,但也可以又是一个 if 语句节点,即类似这样的结构: if (a) { //... } else if (b) { // ... }。 alternate 当然也可以为 null。
                interface IfStatement <: Statement {
                    type: "IfStatement";
                    test: Expression;
                    consequent: Statement;
                    alternate: Statement | null;
                }
        6.11 SwitchStatement // switch 语句节点,有两个属性,discriminant 属性表示 switch 语句后紧随的表达式,通常会是一个变量,cases 属性是一个 case 节点的数组,用来表示各个 case 语句。
                interface SwitchStatement <: Statement {
                    type: "SwitchStatement";
                    discriminant: Expression;
                    cases: [ SwitchCase ];
                }
        6.12 SwitchCase // witch 的 case 节点。test 属性代表这个 case 的判断表达式,consequent 则是这个 case 的执行语句。当 test 属性是 null 时,则是表示 default 这个 case 节点
                interface SwitchCase <: Node {
                    type: "SwitchCase";
                    test: Expression | null;
                    consequent: [ Statement ];
                }
        6.13 ThrowStatement // throw 语句节点,argument 属性用以表示 throw 后边紧跟的表达式。
        6.14 TryStatement // try 语句节点,block 属性表示 try 的执行语句,通常是一个块语句。hanlder 属性是指 catch 节点,finalizer 是指 finally 语句节点,当 hanlder 为 null 时,finalizer 必须是一个块语句节点。
                interface TryStatement <: Statement {
                    type: "TryStatement";
                    block: BlockStatement;
                    handler: CatchClause | null;
                    finalizer: BlockStatement | null;
                }
        6.15 CatchClause // catch 节点,param 用以表示 catch 后的参数,body 则表示 catch 后的执行语句,通常是一个块语句。
                interface CatchClause <: Node {
                    type: "CatchClause";
                    param: Pattern;
                    body: BlockStatement;
                }
        6.16 WhileStatement // while 语句节点,test 表示括号中的表达式,body 是表示要循环执行的语句。
                interface WhileStatement <: Statement {
                    type: "WhileStatement";
                    test: Expression;
                    body: Statement;
                }
        6.17 DoWhileStatement // do/while 语句节点,和 while 语句类似。
        6.18 ForStatement // for 循环语句节点,属性 init/test/update 分别表示了 for 语句括号中的三个表达式,初始化值,循环判断条件,每次循环执行的变量更新语句(init 可以是变量声明或者表达式)。这三个属性都可以为 null,即 for(;;){}。 body 属性用以表示要循环执行的语句。
                interface ForStatement <: Statement {
                    type: "ForStatement";
                    init: VariableDeclaration | Expression | null;
                    test: Expression | null;
                    update: Expression | null;
                    body: Statement;
                }
        6.19 ForInStatement // for/in 语句节点,left 和 right 属性分别表示在 in 关键词左右的语句(左侧可以是一个变量声明或者表达式)。body 依旧是表示要循环执行的语句。
                interface ForInStatement <: Statement {
                    type: "ForInStatement";
                    left: VariableDeclaration |  Pattern;
                    right: Expression;
                    body: Statement;
                }
    7. Declarations // 声明语句节点,同样也是语句,只是一个类型的细化。下边会介绍各种声明语句类型。

        7.1 FunctionDeclaration //函数声明,和之前提到的 Function 不同的是,id 不能为 null。
                interface FunctionDeclaration <: Function, Declaration {
                    type: "FunctionDeclaration";
                    id: Identifier;
                }
        7.2 VariableDeclaration // 变量声明,kind 属性表示是什么类型的声明,因为 ES6 引入了 const/let。declarations 表示声明的多个描述,因为我们可以这样:let a = 1, b = 2;。
                interface VariableDeclaration <: Declaration {
                    type: "VariableDeclaration";
                    declarations: [ VariableDeclarator ];
                    kind: "var";
                }
        7.3 VariableDeclarator // 变量声明的描述,id 表示变量名称节点,init 表示初始值的表达式,可以为 null。
                interface VariableDeclarator <: Node {
                    type: "VariableDeclarator";
                    id: Pattern;
                    init: Expression | null;
                }
    8. Expressions // 表达式节点。

        8.1 ThisExpression // 表示 this。
        8.2 ArrayExpression //数组表达式节点,elements 属性是一个数组,表示数组的多个元素,每一个元素都是一个表达式节点。
                interface ArrayExpression <: Expression {
                    type: "ArrayExpression";
                    elements: [ Expression | null ];
                }
        8.3 ObjectExpression // 对象表达式节点,property 属性是一个数组,表示对象的每一个键值对,每一个元素都是一个属性节点。
                interface ObjectExpression <: Expression {
                    type: "ObjectExpression";
                    properties: [ Property ];
                }
        8.4 Property // 对象表达式中的属性节点。key 表示键,value 表示值,由于 ES5 语法中有 get/set 的存在,所以有一个 kind 属性,用来表示是普通的初始化,或者是 get/set。
                interface Property <: Node {
                    type: "Property";
                    key: Literal | Identifier;
                    value: Expression;
                    kind: "init" | "get" | "set";
                }
        8.5 FunctionExpression // 函数表达式节点。
        8.6 UnaryExpression // 一元运算表达式节点(++/-- 是 update 运算符,不在这个范畴内),operator 表示运算符,prefix 表示是否为前缀运算符。argument 是要执行运算的表达式。
                interface UnaryExpression <: Expression {
                    type: "UnaryExpression";
                    operator: UnaryOperator;
                    prefix: boolean;
                    argument: Expression;
                }
        8.7 UnaryOperator // 一元运算符,枚举类型
                enum UnaryOperator {
                    "-" | "+" | "!" | "~" | "typeof" | "void" | "delete"
                }
        8.8 UpdateExpression // update 运算表达式节点,即 ++/--,和一元运算符类似,只是 operator 指向的节点对象类型不同,这里是 update 运算符。
                interface UpdateExpression <: Expression {
                    type: "UpdateExpression";
                    operator: UpdateOperator;
                    argument: Expression;
                    prefix: boolean;
                }
        8.9 UpdateOperator //update 运算符,值为 ++ 或 --,配合 update 表达式节点的 prefix 属性来表示前后。
                enum UpdateOperator {
                    "++" | "--"
                }
        8.10 BinaryExpression // 二元运算表达式节点,left 和 right 表示运算符左右的两个表达式,operator 表示一个二元运算符
                interface BinaryExpression <: Expression {
                    type: "BinaryExpression";
                    operator: BinaryOperator;
                    left: Expression;
                    right: Expression;
                }
        8.11 BinaryOperator
                enum BinaryOperator {
                    "==" | "!=" | "===" | "!=="
                        | "<" | "<=" | ">" | ">="
                        | "<<" | ">>" | ">>>"
                        | "+" | "-" | "*" | "/" | "%"
                        | "|" | "^" | "&" | "in"
                        | "instanceof"
                }
        8.12 AssignmentExpression // 赋值表达式节点,operator 属性表示一个赋值运算符,left 和 right 是赋值运算符左右的表达式。
                interface AssignmentExpression <: Expression {
                    type: "AssignmentExpression";
                    operator: AssignmentOperator;
                    left: Pattern | Expression;
                    right: Expression;
                }
        8.13 AssignmentOperator
                enum AssignmentOperator {
                    "=" | "+=" | "-=" | "*=" | "/=" | "%="
                        | "<<=" | ">>=" | ">>>="
                        | "|=" | "^=" | "&="
                }
        8.14 LogicalExpression // 逻辑运算表达式节点,和赋值或者二元运算类型,只不过 operator 是逻辑运算符类型。
                interface LogicalExpression <: Expression {
                    type: "LogicalExpression";
                    operator: LogicalOperator;
                    left: Expression;
                    right: Expression;
                }
        8.15 LogicalOperator
                enum LogicalOperator {
                    "||" | "&&"
                }
        8.16 MemberExpression // 成员表达式节点,即表示引用对象成员的语句,object 是引用对象的表达式节点,property 是表示属性名称,computed 如果为 false,是表示 . 来引用成员,property 应该为一个 Identifier 节点,如果 computed 属性为 true,则是 [] 来进行引用,即 property 是一个 Expression 节点,名称是表达式的结果值。
                interface MemberExpression <: Expression, Pattern {
                    type: "MemberExpression";
                    object: Expression;
                    property: Expression;
                    computed: boolean;
                }
        8.17 ConditionalExpression // 条件表达式,通常我们称之为三元运算表达式,即 boolean ? true : false。属性参考条件语句
                interface ConditionalExpression <: Expression {
                    type: "ConditionalExpression";
                    test: Expression;
                    alternate: Expression;
                    consequent: Expression;
                }
        8.18 CallExpression // 函数调用表达式,即表示了 func(1, 2) 这一类型的语句。callee 属性是一个表达式节点,表示函数,arguments 是一个数组,元素是表达式节点,表示函数参数列表。
                interface CallExpression <: Expression {
                    type: "CallExpression";
                    callee: Expression;
                    arguments: [ Expression ];
                }
        8.19 NewExpression // new 表达式。
        8.20 SequenceExpression // 这个就是逗号运算符构建的表达式,expressions 属性为一个数组,即表示构成整个表达式,被逗号分割的多个表达式。
                interface SequenceExpression <: Expression {
                    type: "SequenceExpression";
                    expressions: [ Expression ];
                }