实现一个babel插件

Posted by franki on May 3, 2020

进入es6时代,大家都知道有很多浏览器是不支持es6代码的,但是为啥可以打包出来的代码可以跑在浏览器上呢?相信大家有会说用babel-loader转义,把es6代码转化成浏览器认识的es5代码,可是大家都知道babel是如何转化es6代码的吗?里面究竟发生了什么不可告人的秘密,今天就好好探索下。

开宗明义,先来讲下babel的核心原理:

Babel 解析成 AST,然后插件更改 AST,最后 Babel 输出代码

具体是:

1 babylon 解析器把代码字符串转化为 AST(abstract syntax tree)树

2 babel-traverse 对 AST 树进行解析遍历出整个树的path

3 plugin 转换出新的 AST 树

4 生成新的代码字符串

下面来尝试实现一个插件,作用是把

const res = 1 + 1;
// 转化为
const res = 2;

babel 插件的核心是需要返回一个 function,function 内返回 visitor

module.export = function() {
  return {
    visitor: {
    }
  }
}

visitor (访问者)是对各类型的 AST 节点做处理的地方,问题是 visitor 怎么知道 Babel 生成了的 AST 有哪些节点?

答案是可以通过打印Babel转化过后的代码。这里可以帮助你快速解决代码转化问题:

AST explorer

转化后的样子如下图:

ast

一定有小伙伴会问,为什么会要有 AST 这个东西?

但是语言的的编译过程需要经历两个阶段:

  • 词法分析
  • 语法分析

AST 就是等于把一些列的单词都拆出来,这样的好处是让计算机更容易识别,AST 充当了翻译官的角色,让编写代码的人和机器都很容易知道代码的含义。

语法分析,就是把相关的单词组合起来,就是英文单词都需要组合成句子短语才有其意义,计算机也是如此。

好了,交代上面 AST 的作用后,正式进入编写 babel 插件的工作。

如上,可以看到const res = 1 + 1;其中1 + 1是一个BinaryExpression节点,那么接下来,就来处理这个节点。

const babel = require('babel-core');
var t = require("babel-types");

const visitor = {
    BinaryExpression(path) {
        const node = path.node;
        let result;
        // 表达式两边是否都是数字
        if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {
            // 根据不同的操作符做运算
            switch (node.operator) {
                case "+":
                    result = node.left.value + node.right.value;
                    break;
                case "-":
                    result = node.left.value - node.right.value;
                    break;
                case "*":
                    result = node.left.value * node.right.value;
                    break;
                case "/":
                    result = node.left.value / node.right.value;
                    break;
                default:
                    break;
            }
        }
        if (result !== undefined) {
            path.replaceWith(t.numericLiteral(result));
        }
    }
};

module.exports = function (params) {
    return {
        visitor
    }
}

哈哈,以上就是最简单的一个babel插件了,

具体如何使用呢?

  1. 配置到node_modules

    可以新建一个文件夹叫babel-plugin-simple-plus,另外在其下面新建一个文件叫做index.js,内容为上面的代码。具体如图。

    babel-plugin-simple-plus

  2. 在.babelrc加入配置项

{
    "presets": ["@babel/preset-env"],
    "plugins": [
        "@babel/plugin-proposal-class-properties",
        "simple-plus"
    ]
}

只需要加入“simple-plus”配置即可。

小结:

其他更多的 babel 插件玩法,需要各位高玩自己好好琢磨(自己也需要继续探索更多高深用法),可以根据项目的需要,写出适合项目的 babel 插件,提高工作效率比什么都重要哈,在这里面还了解了下编译原理,仔细读了下,发现也没有一开始的恐惧感,其实编译最核心还是把代码字符串告诉计算机,使得计算机认识我们平常编写的代码,上面AST就很大程度上帮助我们解决这类问题。