写一个Babel插件

写一个Babel插件

什么是Babel?

来看一下官方解释:

Babel 是一个 JavaScript 编译器。

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,
以便能够运行在当前和旧版本的浏览器或其他环境中。

作为一种语言,JavaScript 在不断发展,新的标准/提案和新的特性层出不穷。 在得到广泛普及之前,Babel 能够让你提前(甚至数年)使用它们。

Babel的原理是什么?

一图胜千言

在这里插入图片描述

可以看到Babel主要做了三件事:解析 parse 、转换 transform 、生成 generate 。

1. 解析 parse

解析阶段的产物是 abstract syntax tree ,AST抽象语法树。

解析有词法分析和语法分析两个步骤。

  1. 词法分析

词法分析(英语:lexical analysis)是计算机科学中将字符序列转换为标记(token)序列的过程。

语法分析阶段,把字符串形式的代码转换为tokens,可以理解为一组标记数组。

词法分析类似于我们的分词过程,“我 想吃 火锅”,名词、动词、名词。

比如sum = 2 + 3,标识符、操作符、数字、操作符、数字。

[
  { type: { ... }, value: "sum", start: 0, end: 2 },
  { type: { ... }, value: "=", start: 3, end: 4 },
  { type: { ... }, value: "2", start: 4, end: 5 },
  ...
]

Lexical_analysis

词法分析

  1. 语法分析

进行语法检查、并构建由输入的单词组成的数据结构

语法分析

简单点,说话的方式简单点,就是把 tokens 转换成 AST抽象语法树。

AST抽象语法树

2. 转换 transform

转换阶段接收一棵AST抽象语法树,对其进行遍历,做一些添加节点、删除节点、修改节点的操作,并输出转换后的AST抽象语法树。

3. 生成 generate

这一阶段也很好理解,接收转换好的AST抽象语法树,生成字符串形式的代码,并创建源码映射。

Introduction to JavaScript Source Maps

JavaScript Source Map 详解


Babel 实际上是一组模块的集合。

  • babylon 是 Babel 的解析器。

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

  • babel-generator 模块是 Babel 的代码生成器,它读取AST并将其转换为代码和源码映射(sourcemaps)。

  • babel-types 模块是一个用于 AST 节点的 Lodash 式工具库,它包含了构造、验证以及变换 AST 节点的方法。

Babel插件是做什么的?

Babel’s code transformations are enabled by applying plugins (or presets) to your configuration file.

Babel插件

插件是干什么的呢?

直译一下,Babel的代码转换得益于在配置文件中设置的插件和预设的应用。

Visitors(访问者)和 Paths(路径)

开始写plugin之前,我们还需要了解一下如何访问语法树的节点,节点与节点之间又是怎样关联的。

Visitors(访问者)

访问者模式是一种将数据操作和数据结构分离的设计模式。

简单的说,访问者就是一个对象,定义了用于在一个树状结构中获取具体节点的方法。

看一下官方例子:

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

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

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

这段代码会触发几次"Called!"打印?

可以看一下这段代码解析出来的AST抽象语法树

有四个Identifier,所以触发四次。

实际上,Identifier() { ... }Identifier: { enter() { ... } } 的简写形式,一个访问者有两次机会访问节点:进入节点,退出节点。

const MyVisitor = {
  "Identifier|FunctionDeclaration|BlockStatement|ReturnStatement|BinaryExpression": {
        enter(path) {
            console.log("Entered!",path.node.type,path.node.name || '');
        },
        exit(path) {
            console.log("Exited!",path.node.type,path.node.name || '');
        }
    }
};

在这里插入图片描述

可以把方法名用"|"分割成"Idenfifier|MemberExpression"形式的字符串,把同一个函数应用到多种访问节点。

Paths(路径)

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

当我们通过Visitor来访问节点时,实际访问当不是节点,而是路径。

const MyVisitor = {
    "Identifier": {
        enter(path) {
            if(path.node.name === 'a') console.log(path);
        },
    }
}

用上面这个访问者访问var a = 1的AST抽象语法树,我们可以看到此时的path:

在这里插入图片描述

路径对象还包含添加、更新、移动和删除节点有关的其他很多方法。


当然path中还有很多其他信息,有兴趣可以去了解一下

{
  "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
}

写一个Babel插件吧

来写一个删除console.log(...)的插件吧。

plugin 是一个接收了当前babel对象作为参数的 function,我们先把babel.types取出来,我们将用到它的一些验证节点类型的方法。

module.exports = function({ types: t }) {
    return {
        visitor: {
            // ...
        }
    }
}

来看看一条console.log(...)语句的AST抽象语法树吧。

在这里插入图片描述

然后就可以完成这个插件了!

CallExpression(path, state) {
    let node = path.node.callee
    if(t.isMemberExpression(node) 
        && t.isIdentifier(node.object) 
        && node.object.name === 'console' 
        && t.isIdentifier(node.property) 
        && node.property.name === 'log' ){
            path.parentPath.remove();
    }
},

在这里插入图片描述

一个小知识

  1. 插件的执行顺序是怎样的?

插件顺序从前往后排列。

  1. 预设的执行顺序是怎样的?

Preset 顺序是颠倒的(从后往前)。

  1. 插件和预设的执行顺序是怎样的?

插件在 Presets 前运行。

相关文档

Babel插件手册

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值