babel是一个转码器,目前开发react、vue项目都要使用到它。它可以把es6+的语法转换为es5,也可以转换JSX等语法。
我们在项目中都是通过配置插件和预设(多个插件的集合)来转换特定代码,例如env、stage-0等。
实际上babel可以通过自定义插件的方式实现任何代码的转换,接下来我们通过一个“把es6的 class
转换为es5”的例子来了解一下babel。
内容如下:
webpack环境配置
大家应该都配置过babel-core这个loader,它的作用是提供babel的核心Api,实际上我们的代码转换都是通过插件来实现的。
接下来我们不用第三方的插件,自己实现一个es6类转换插件。先执行以下几步初始化一个项目:
- npm install webpack webpack-cli babel-core -D
- 新建一个webpack.config.js
- 配置webpack.config.js
如果我们的插件名字想叫transform-class,需要在webpack配置中做如下配置:
接下来我们在node_modules中新建一个babel-plugin-transform-class的文件夹来写插件的逻辑(如果是真实项目,你需要编写这个插件并发布到npm仓库),如下图:
红色区域是我新建的文件夹,它上面的是一个标准的插件的项目结构,为了方便我只写了核心的index.js文件。
如何编写bable插件
babel插件其实是通过AST(抽象语法树)实现的。
babel帮助我们把js代码转换为AST,然后允许我们修改,最后再把它转换成js代码。
那么就涉及到两个问题:js代码和AST之间的映射关系是什么?如何替换或者新增AST?
好,先介绍一个工具:astexplorer.net:
这个工具可以把一段代码转换为AST:
如图,我们写了一个es6的类,然后网页的右边帮我们生成了一个AST,其实就是把每一行代码变成了一个对象,这样我们就实现了一个映射。
再介绍一个文档: babel-types :
这是创建AST节点的api文档。
比如,我们想创建一个类,先到astexplorer.net中转换,发现类对应的AST类型是 ClassDeclaration
。好,我们去文档中搜索,发现调用下面的api就可以了:
创建其他语句也是一样的道理,有了上面这两个东西,我们可以做任何转换了。
下面我们开始真正编写一个插件,分为以下几步:
- 在index.js中export一个函数
- 函数中返回一个对象,对象有一个visitor参数(必须叫visitor)
- 通过astexplorer.net查询出
class
对应的AST节点为ClassDeclaration
- 在vistor中设置一个捕获函数
ClassDeclaration
,意思是我要捕获js代码中所有ClassDeclaration
节点 - 编写逻辑代码,完成转换
module.exports = function ({ types: t }) { return { visitor: { ClassDeclaration(path) { //在这里完成转换 } } }; }
代码中有两个参数,第一个 {types:t}
东西是从参数中解构出变量t,它其实就是babel-types文档中的t(下图红框),它是用来创建节点的:
第二个参数 path
,它是捕获到的节点对应的信息,我们可以通过 path.node
获得这个节点的AST,在这个基础上进行修改就能完成了我们的目标。
如何把es6的class转换为es5的类
上面都是预备工作,真正的逻辑从现在才开始,我们先考虑两个问题:
我们要做如下转换,首先把es6的类,转换为es5的类写法(也就是普通函数),我们观察到,很多代码是可以复用的,包括函数名字、函数内部的代码块等。
如果不定义class中的 constructor
方法,JavaScript引擎会自动为它添加一个空的 constructor()
方法,这需要我们做兼容处理。
接下来我们开始写代码,思路是:
- 拿到老的AST节点
- 创建一个数组用来盛放新的AST节点(虽然原class只是一个节点,但是替换后它会被若干个函数节点取代) 初始化默认的
constructor
节点(上文提到,class中有可能没有定义constructor) - 循环老节点的AST对象(会循环出若干个函数节点)
- 判断函数的类型是不是
constructor
,如果是,通过取到数据创建一个普通函数节点,并更新默认constructor
节点 - 处理其余不是
constructor
的节点,通过数据创建prototype
类型的函数,并放到es5Fns
中 - 循环结束,把
constructor
节点也放到es5Fns
中 - 判断es5Fns的长度是否大于1,如果大于1使用
replaceWithMultiple
这个API更新AST
module.exports = function ({ types: t }) { return { visitor: { ClassDeclaration(path) { //拿到老的AST节点 let node = path.node let className = node.id.name let classInner = node.body.body //创建一个数组用来成盛放新生成AST let es5Fns = [] //初始化默认的constructor节点 let newConstructorId = t.identifier(className) let constructorFn = t.functionDeclaration(newConstructorId, [t.identifier('')], t.blockStatement([]), false, false) //循环老节点的AST对象 for (let i = 0; i < classInner.length; i++) { let item = classInner[i] //判断函数的类型是不是constructor if (item.kind == 'constructor') { let constructorParams = item.params.length "=", left, right) es5Fns.push(protoTypeExpression) } } //循环结束,把constructor节点也放到es5Fns中 es5Fns.push(constructorFn) //判断es5Fns的长度是否大于1 if (es5Fns.length > 1) { path.replaceWithMultiple(es5Fns) } else { path.replaceWith(constructorFn) } } } }; }
优化继承
其实,类还涉及到继承,思路也不复杂,就是判断AST中没有 superClass
属性,如果有的话,我们需要多添加一行代码 Bird.prototype = Object.create(Parent)
,当然别忘了处理 super
关键字。
打包后代码
运行 npm start
打包后,我们看到打包后的文件里 class
语法已经成功转换为一个个的es5函数。
结尾
现在一个类转换器就写完了,希望能对大家了解babel有一点帮助。也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]