es6/7/8的出现,给我们带来了很多方便,但是浏览器并不怎么支持,目前chrome应该是支持率最高的,所以为了兼容我们只能把代码转成es5,这应该算是我们最初使用babel的一个缘由,随着业务的开发,我们会有很多自己定制化的需求,单纯的bebel并不能解决我们所有的问题,所以babel插件应用而来,本文将会采用较为通俗的语言来描述如何快速写一个babel插件。

一、babel的作用

babel的作用其实就是一个转换器,把我们的代码转成浏览器可以运行的代码,类似于加工厂的概念。解析代码都是一个文件一个文件的处理,把代码读出来,然后经过处理,再输出,在处理的过程中每个文件的代码其实就是个大的字符串。但是我们要把有些语法修改,比如let定义变量改成var定义,很明显用字符串替换是不现实的,这里babel是把代码转成ast语法树,然后经过一系列操作之后再转成字符串输出,

二、ast分析

那么什么是ast语法树呢?

比如代码var a = 12对应的就是下面的ast语法树,是不是很懵?就这么简单的代码弄出这么多东西



先来个个人官方解释,ast->Abstract Syntax Tree,也叫抽象语法树,就是对代码进行词法分析之后再进行语法分析弄出来的东西,可以理解为代码执行前的编译过程,毕竟运行代码的不是我们,所以要变成机器可是别的东东。

简单分析下var a = 12这一句,首先我们知道这条语句是定义变量,定义了a,并且赋值12(非配内存什么的这里就不说了,跟理解ast没啥用处),然后我们在对应看那个ast语法树,开始的program就是根节点,不用管,然后是body,应该是到重点了,紧接着我们就看到了

这句VariableDeclaration字面意思就是变量声名,是不是跟我们之前的分析对应上了,至于接下来为啥又有个declarations数组,也很好理解,我们声明变量是不是可以用逗号隔开同时写多个,就像var a = 12, b = 16;,而他们在同一条声明语句中,所以就用数组来表示了。再看具体的一个



一个id,一个init,也很直观的看出,id就是我们的变量名,init就是我们的值,然后我们看到有三个key在每个花括号中都是一直在重复出现,就是type/start/end,type就是类型,start是代码起始位置,end是结束位置,关于type这里多介绍点。

type

这里我们把每个花括号叫做一个节点,每个节点代表了代码中的一部分,变量名,然后是所赋的值,type表示了每一块节点所表示的类型,那么这些类型有那些呢,babel type这里有详细介绍,当然有很多种类型,这里也无法给大家一一讲解(我也不知道所有的),但是我们需要知道的只是我们要改变的代码是什么类型的节点,介绍个网址 AST explorer,在这个页面中我们只需要把代码写进去,就会展示出代码的ast语法树,上面截图就是来自于这个网站。

三、写插件

基础的东西讲了写,下面说下具体如何写插件。

插件格式



这是一个插件的基本格式,一个函数,参数是babel,这里我们用到的是types这个属性,所以只把它写出来,然后就是返回一个对象,key是visiter,然后里面又是个对象,但是key是我们熟悉的东西,就是一个babel-types类型,然后是一个箭头函数,函数有两个参数,path表示路径,state表示状态。

visitor字面意思就是访问者,这里也是这个意思,也就是我们要访问哪个类型的节点,这里是个CallExpression,字面意思就是调用表达式,类似于handle(),path参数表示当前节点的位置,包含的主要是当前节点(node)内容以及父节点(parent)内容,state先不管,有了这些我们就可以去修改代码了。

一个简单的插件

我们先来一个简单的插件,要求是把所有定义变量名为a的换成是b

首先我们要找到定义变量的地方,然后判断变量名是不是a,如果是就把它替换成b,思路就是这样。开始动手,首先把这一句放到 AST explorer这个网站中,鼠标选中这一句代码,右侧就会展示出这句代码转成ast的样子



我们看到这是一个变量定义的语句,所以我们要找的节点类型就是VariableDeclarator,所以写成如下

visitor: {
VariableDeclarator: (path, state) => {
//code
}
}

然后我们要判断他的变量名是不是a,可以从ast中看到VariableDeclarator的id属性就是变量名部分,所以我们只要判断id的name属性是不是a就可以了

//访问的是当前节点,所以操作对象是path.node
if(path.node.id.name === 'a'){
//code
}

有人可能会想这里是不是直接path.node.id.name = 'b'就可以了,如果你是操作object,那你就对了,不过这里是ast语法树,所以想改变某个值,就是用对应的ast来替换,所以我们要把id是a的ast换成b的ast,那么b的ast怎么创建呢?很简单,最外层的函数参数我们引入了types,就是用这个来构建,替换的类型是个Identifier所以我们也要构建b的Identifier,写起来就是t.Identifier('b'),所以我们的插件最终就是:

module.exports = function(babel){
let t = babel.types;
return {
visitor: {
VariableDeclarator(path, state) {
if(path.node.id.name == 'a'){
path.node.id = t.Identifier('b')
}
}
}
}
}

所以我们写插件的时候只需要以下几个步骤就可以完成:

1.确认我们要修改的节点类型(把代码复制到ast explorer 中,一一对应)

2.找到修改的属性是哪个(这里我们修改id属性)

3.根据旧的ast构建新的ast语句并替换(把b构建成ast语句替换原来的属性)

构建ast

这里的一个难点就是如何构建ast,代码有很多种类型,我们要修改就需要构建各种各样类型的ast,这里我们结合AST explorerbabel type来快速构建,首先你要知道我们要构建的ast是什么样的,所以把代码放到AST explorer中,我们就可以在右侧看到它的ast树,然后再根据节点类型,参考babel type的使用方法一层一层的构建。

下面举个例子:



我们要构建声明语句,第一层节点类型是VariableDeclaration,所以要写这个类型的ast,看下babel-type中怎么用



照着写t.variableDeclaration('const', [declarators]) ,kind是const,后面是个数组,每一项是个VariableDeclarator。

我们再看ast,下一层就是个VariableDeclarator,还是去查下这个怎么用一个id,一个init,id就是变量名,init就是要赋的值,所以写出来就是t.variableDeclarator('info', initExpression),是这样吗?要记住每一项都是ast,所以info也要构建成ast,看下ast树中这个id是啥样的,他是个identifier,然后使用方法是

所以id就是t.identifier('info'),init表达式有点复杂,是个对象,所以继续上面的步骤,写出来如下

t.objectExpression([t.objectProperty(
t.identifier('name'),
t.stringLiteral('Steven'),
false,
false,
null
)])

连起来就是

 t.variableDeclaration('let', [t.variableDeclarator(t.identifier('info'), t.objectExpression([t.objectProperty(
t.identifier('name'),
t.stringLiteral('Steven'),
false,
false,
null
)]))])

我们来验证下



包括之前的把a换成b

显然是对的,插件中内容是

四、总结

写插件最快的方法就是,对照上面推荐的两个网站一层一层的构建ast,然后做想要的操作。在GitHub上有全面的介绍写插件的各个属性及方法,看这里教程

快速写一个babel插件的更多相关文章

  1. 用express快速写一个hello world

    首页要具备 node.js环境, npm环境 创建一个目录, 然后进入此目录作为工作目录 mkdir myapp cd myapp 通过npm init 创建一个package.json文件 npm ...

  2. 教你快速写一个EventBus框架

    前言EventBus相信大多数人都用过,其具有方便灵活.解耦性强.体积小.简单易用等优点,虽然现在也有很多优秀的替代方案如RxBus.LiveDataBus等,但不可否认EventBus开创了消息总线 ...

  3. 开发一个简单的babel插件

    前言 对于前端开发而言,babel肯定是再熟悉不过了,工作中肯定会用到.除了用作转换es6和jsx的工具之外,个人感觉babel基于抽象语法树的插件机制,给我们提供了更多的可能.关于babel相关概念 ...

  4. 【记录】如何造一个vite插件(1)

    在看文章前,先做个定位,这不是一篇纯粹的技术性文章,可以把它理解成一个叙述文章,记录我开发插件的过程. 开始前简单的吹个牛 vue2 也写了很多年了,多人合作始终避不开用到别人的组件.关键是有些组件没 ...

  5. 【babel+小程序】记“编写babel插件”与“通过语法解析替换小程序路由表”的经历

    话不多说先上图,简要说明一下干了些什么事.图可能太模糊,可以点svg看看 背景 最近公司开展了小程序的业务,派我去负责这一块的业务,其中需要处理的一个问题是接入我们web开发的传统架构--模块化开发. ...

  6. 第40篇 使用Sublime+MarkDown快速写博客

    原文地址:http://blog.laofu.online/2017/06/03/how-use-sublime/ 前端的开发人员应该都知道sublime的神器,今天就说说如何使用sublime结合m ...

  7. [转] 以 async/await 为例,说明 babel 插件怎么搭

    你一定碰到过这些库 babel-polyfill 项目地址:https://github.com/babel/babel/blob/master/packages/babel-polyfill 通过两 ...

  8. 使用babel插件集

    1).打开配置文件".babelrc",配置插件集,代码如下: { "presets":["latest"] } 2).安装babel插件集 ...

  9. 你真的需要一个jQuery插件吗

    jQuery的插件提供了一个很好的方法,节省了时间和简化了开发,避免程序员从头开始编写每个组件.但是,插件也将一个不稳定因素引入代码中.一个好的插件节省了无数的开发时间,一个质量不好的插件会导致修复错 ...

随机推荐

  1. 总结PHP删除字符串最后一个字符的三种方法

    一.前言 从数据库中select()读取一对多的信息时,经常需要将取出的数组用某个特定的字符分割,然后拼接成字符串. 常见的语法格式: foreach ($arr as $key => $val ...

  2. 使用Nginx 做负载均衡

    Nginx可以作为一个非常高效的负载均衡系统,通过分发HTTP请求到多个应用服务器来提高整个系统的吞吐量,性能和可用性. 负载均衡的算法/机制 下面是Nginx支持的机制 轮询机制 轮询算法 最少连接 ...

  3. select下拉框之默认选项清空

    最近和小伙伴发现,select默认选项一般是提示信息,怎么才能让当我们点击下拉框时,可选的选项中没有默认的提示信息呢? 思路: 1.当点击下拉框时,让默认提示信息,即下拉框第一个选项移除. 2.当没有 ...

  4. MVC学习十二:Ajax.ActionLink用法

    Ajax.ActionLink用法 <!--使用Ajax.BeginForm必须引用的js文件--> <script type="text/javascript" ...

  5. PAT——1012. 数字分类

    给定一系列正整数,请按要求对数字进行分类,并输出以下5个数字: A1 = 能被5整除的数字中所有偶数的和: A2 = 将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2+n3-n4...: ...

  6. Linux 文件压缩与解压相关

    tar [-cxtzjvfpPN] 文件与目录 .... 参数:-c :建立一个压缩文件的参数指令-x :解开一个压缩文件的参数指令 -t :查看压缩文件里面的文件 特别注意: c/x/t 同时只能存 ...

  7. Oracle中case的第二种用法

    procedure P_GetProVerSingInfo_2018(varFileID in varchar2, p_cr1 out refcontent, p_cr2 out refcontent ...

  8. 【Graphql实践】使用 Apollo(iOS) 访问 Github 的 Graphql API

    最近在协助调研 Apollo 生成的代码是否有可能跨 Query 共享模型的问题,虽然初步结论是不能,并不是预期的结果,但是在调研过程中积累的一些经验,有必要记录下.如果你也对 Graphql 感兴趣 ...

  9. ComboBox可搜索下拉框的使用注意事项,简单记录以及我遇到的一些奇怪的bug

    前几天做一个react的项目的时候需要用一个可搜索的下拉框ComboBox,上代码: <ComboBox // className={comboxClassName} items={storeA ...

  10. MySQL---视图、触发器

    一.视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,并可以将其当作表来使用. SELECT * FROM ( S ...