babel原理和插件编写

babel是一个多用的多功能的javascript编译器。主要将js代码转化为浏览器或其他特定环境下的代码,比如转换为es5格式,其主要原理是ASTs(抽象语法树),babel在处理代码的每一步都涉及或创建语法树。

什么是AST:

AST(Abstract Syntax Tree) 抽象语法树 /ˈæbstrækt ˈsɪntæks/

谁在使用:webpack和Lint等很多的工具和库的核心都是通过Abstract Syntax Tree抽象语法树这个概念来实现对代码的检查、分析等操作的。通过了解抽象语法树这个概念,你也可以随手编写类似的工具

原理:通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作

用途:在计算机科学中,抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。

ast实例:

function square(n) {
  return n * n;
}

//转化后:
{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "square"
  },
  params: [{
    type: "Identifier",
    name: "n"
  }],
  body: {
    type: "BlockStatement",
    body: [{
      type: "ReturnStatement",
      argument: {
        type: "BinaryExpression",
        operator: "*",
        left: {
          type: "Identifier",
          name: "n"
        },
        right: {
          type: "Identifier",
          name: "n"
        }
      }
    }]
  }
}

每一层AST都有相同的结构:

{
  type: "FunctionDeclaration",
  id: {...},
  params: [...],
  body: {...}
}
{
  type: "Identifier",
  name: ...
}
{
  type: "BinaryExpression",
  operator: ...,
  left: {...},
  right: {...}
}

这样的每一层结构也被叫做 节点(Node)。 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。

字符串形式的 type 字段表示节点的类型(如: "FunctionDeclaration""Identifier",或 "BinaryExpression")。

Babel 还为每个节点额外生成了一些属性,用于描述该节点在原始代码中的位置。

{
  type: ...,
  start: 0,
  end: 38,
  loc: {
    start: {
      line: 1,
      column: 0
    },
    end: {
      line: 3,
      column: 1
    }
  },
  ...
}

每一个节点都会有 startendloc 这几个属性。

————————————————————————————————————————————————————

babel处理流程:

parse => transform => generate

转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程 同时也是插件将要介入工作的部分.

babel访问者模式:Visitors(访问者)

访问者是一个用于 AST 遍历的跨语言的模式。

1,visitors是一个对象

2,定义了用于在一个树状结构中获取具体节点的方法。

实例:

const MyVisitor = {
  Identifier() {
    console.log("Called!");
  }
};

这是一个简单的访问者,把它用于遍历中时,每当在树中遇见一个 Identifier 的时候会调用 Identifier() 方法。

Paths路径:

Path 是表示两个节点之间连接的对象。

path结构:

将子节点 Identifier 表示为一个路径(Path)的话,看起来是这样的:

{
  "parent": {
    "type": "FunctionDeclaration",
    "id": {...},
    ....
  },
  "node": {
    "type": "Identifier",
    "name": "square"
  }
}
同时它还包含关于该路径的其他元数据:

{
  "parent": {...},
  "node": {...},
  "hub": {...},
  "contexts": [],
  "data": {},
  "shouldSkip": false,
  "shouldStop": false,
  "removed": false,
  "state": null,
  "opts": null,
  "skipKeys": null,
  "parentPath": null,
  "context": null,
  "container": null,
  "listKey": null,
  "inList": false,
  "parentKey": null,
  "key": null,
  "scope": null,
  "type": null,
  "typeAnnotation": null
}

State(状态)

使用递归消除全局状态,把一个访问者放进另外一个访问者里面。

const updateParamNameVisitor = {
  Identifier(path) {
    if (path.node.name === this.paramName) {
      path.node.name = "x";
    }
  }
};

const MyVisitor = {
  FunctionDeclaration(path) {
    const param = path.node.params[0];
    const paramName = param.name;
    param.name = "x";

    path.traverse(updateParamNameVisitor, { paramName });
  }
};

path.traverse(MyVisitor);

Scopes(作用域)

JavaScript 支持词法作用域,在树状嵌套结构中代码块创建出新的作用域

在 JavaScript 中,每当你创建了一个引用,不管是通过变量(variable)、函数(function)、类型(class)、参数(params)、模块导入(import)还是标签(label)等,它都属于当前作用域。

作用域可以被表示为如下形式:

{
  path: path,
  block: path.node,
  parentBlock: path.parent,
  parent: parentScope,
  bindings: [...]
}

绑定:所有引用属于特定的作用域,引用和作用域的这种关系被称作:绑定(binding)。.

常用api介绍(具体参见https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md

Babylon 是 Babel 的解析器。最初是 从Acorn项目fork出来的。Acorn非常快,易于使用,并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。

Babel Traverse(遍历)模块维护了整棵树的状态,并且负责替换、移除和添加节点。

Babel Types模块是一个用于 AST 节点的 Lodash 式工具库(译注:Lodash 是一个 JavaScript 函数工具库,提供了基于函数式编程风格的众多工具函数), 它包含了构造、验证以及变换 AST 节点的方法。

Definitions(定义)

Builders(构建器)

Validators(验证器)

Converters(变换器)babel-generator  babel-template
________________________________________________________

插件编写:

export default function({ types: t }) {
  return {
    visitor: {
      Identifier(path, state) {},
      ASTNodeTypeHere(path, state) {}
    }
  };
};

1,Visitor 中的每个函数接收2个参数:path 和 state

2,由于经常使用到babel.types,所以直接取出 babel.types 会更方便

编写一个基于  foo === bar;  的插件

export default function({ types: t }) {
  return {
    visitor: {
      BinaryExpression(path) {
        if (path.node.operator !== "===") {
            return;
        }

          path.node.left = t.identifier("sebmck");
          path.node.right = t.identifier("dork");
        }
    }
  };
}

更多参见:https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md

 

参考:https://www.jianshu.com/p/b9f14f384954

https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值