2020年5月写的一篇文章,还没在掘金发过。原文链接
前言
最近在尝试玩一玩已经被大家玩腻的 Babel
,今天给大家分享如何用 Babel
为代码自动引入依赖,通过一个简单的例子入门 Babel
插件开发。
需求
同学们都知道,如果运行上面的代码,一定是会报错的:
我们得首先通过 import axuebin from 'axuebin'
引入 axuebin
之后才能使用。。
为了防止这种情况发生(一般来说我们都会手动引入),或者为你省去引入这个包的麻烦(其实有些编译器也会帮我们做了),我们可以在打包阶段分析每个代码文件,把这个事情做了。
在这里,我们就基于最简单的场景做最简单的处理,在代码文件顶部加一句引用语句:
前置知识
什么是 Babel
简单地说,Babel
能够转译 ECMAScript 2015+
的代码,使它在旧的浏览器或者环境中也能够运行。我们日常开发中,都会通过 webpack
使用 babel-loader
对 JavaScript
进行编译。
Babel 是如何工作的
首先得要先了解一个概念:抽象语法树(Abstract Syntax Tree, AST),Babel
本质上就是在操作 AST
来完成代码的转译。
了解了 AST
是什么样的,就可以开始研究 Babel
的工作过程了。
Babel
的功能其实很纯粹,它只是一个编译器。
大多数编译器的工作过程可以分为三部分,如图所示:
- Parse(解析) 将源代码转换成更加抽象的表示方法(例如抽象语法树)
- Transform(转换) 对(抽象语法树)做一些特殊处理,让它符合编译器的期望
- Generate(代码生成) 将第二步经过转换过的(抽象语法树)生成新的代码
所以我们如果想要修改 Code
,就可以在 Transform
阶段做一些事情,也就是操作 AST
。
AST 节点
我们可以看到 AST
中有很多相似的元素,它们都有一个 type
属性,这样的元素被称作节点。一个节点通常含有若干属性,可以用于描述 AST
的部分信息。
比如这是一个最常见的 Identifier
节点:
所以,操作 AST
也就是操作其中的节点,可以增删改这些节点,从而转换成实际需要的 AST
。
更多的节点规范可以查阅 github.com/estree/estr…
AST 遍历
AST
是深度优先遍历的,遍历规则不用我们自己写,我们可以通过特定的语法找到的指定的节点。
Babel
会维护一个称作 Visitor
的对象,这个对象定义了用于 AST
中获取具体节点的方法。
一个 Visitor
一般是这样:
visitor
上挂载以节点 type
命名的方法,当遍历 AST
的时候,如果匹配上 type
,就会执行对应的方法。
操作 AST 的例子
通过上面简单的介绍,我们就可以开始任意造作了,肆意修改 AST
了。先来个简单的例子热热身。
箭头函数是 ES5
不支持的语法,所以 Babel
得把它转换成普通函数,一层层遍历下去,找到了 ArrowFunctionExpression
节点,这时候就需要把它替换成 FunctionDeclaration
节点。所以,箭头函数可能是这样处理的:
开发 Babel 插件的前置工作
在开始写代码之前,我们还有一些事情要做一下:
分析 AST
将原代码和目标代码都解析成 AST
,观察它们的特点,找找看如何增删改 AST
节点,从而达到自己的目的。
我们可以在 astexplorer.net 上完成这个工作,比如文章最初提到的代码:
转换成 AST
之后是这样的:
可以看出,这个 body
数组对应的就是根节点的三条语句,分别是:
- VariableDeclaration:
const a = require('a')
- ImportDeclaration:
import b from 'b'
- ExpressionStatement:
console.log(axuebin.say('hello babel'))
我们可以打开 VariableDeclaration
节点看看:
它包含了一个 declarations
数组,里面有一个 VariableDeclarator
节点,这个节点有 type
、id
、init
等信息,其中 id
指的是表达式声明的变量名,init
指的是声明内容。
通过这样查看/对比 AST
结构,就能分析出原代码和目标代码的特点,然后可以开始动手写程序了。
查看节点规范
节点规范:github.com/estree/estr…
我们要增删改节点,当然要知道节点的一些规范,比如新建一个 ImportDeclaration
需要传递哪些参数。
写代码
准备工作都做好了,那就开始吧。
初始化代码
我们的 index.js
代码为:
然后我们准备一个测试文件 test.js
,代码为:
分析 AST / 编写对应 type 代码
我们这次需要做的事情很简单,做两件事:
- 寻找当前
AST
中是否含有引用axuebin
包的节点 - 如果没引用,则修改
AST
,插入一个ImportDeclaration
节点
我们来分析一下 test.js
的 AST
,看一下这几个节点有什么特征:
ImportDeclaration 节点
ImportDeclaration
节点的 AST
如图所示,我们需要关心的特征是 value
是否等于 axuebin
,
代码这样写:
其中,可以通过 path.get
来获取对应节点的 path
,嗯,比较规范。如果想获取对应的真实节点,还需要 .node
。
满足上述条件则可以认为当前代码已经引入了 axuebin
包,不用再做处理了。
VariableDeclaration 节点
对于 VariableDeclaration
而言,我们需要关心的特征是,它是否是一个 require
语句,并且 require
的是 axuebin
,代码如下:
ExpressionStatement 节点
require('c')
,语句我们一般不会用到,我们也来看一下吧,它对应的是 ExpressionStatement
节点,我们需要关心的特征和 VariableDeclaration
一致,这也是我把 isTrueRequire
抽出来的原因,所以代码如下:
插入引用语句
如果上述分析都没找到代码里引用了 axuebin
,我们就需要手动插入一个引用:
通过 AST
分析,我们发现它是一个 ImportDeclaration
:
简化一下就是这样:
当然,不是直接构建这个对象放进去就好了,需要通过 babel
的语法来构建这个节点(遵循规范):
这样就插入了一个 import
语句。
结果
我们 node index.js
一下,test.js
就变成:
彩蛋
如果我们还想帮他再多做一点事,还能做什么呢?
既然都自动引用了,那当然也要自动安装一下这个包呀!
判断一个应用是否安装了某个依赖,有没有更好的办法呢?
总结
我也是刚开始学 Babel
,希望通过这个 Babel
插件的入门例子,可以让大家了解 Babel
其实并没有那么陌生,大家都可以玩起来 ~
更多文章可以关注公众号「前端试炼」,分享每日前端精选文章。
完整代码见:github.com/axuebin/bab…
- Babel 用户手册
- Babel 插件手册
- ast 分析
- 节点规范
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!