This time I will bring you the implementation of the babel conversion to es6 method. What are the precautions for babel conversion to es6? The following is a practical case, let's take a look.
In our projects, we all convert specific codes, such as env, stage-0, etc., by configuring plug-ins and presets (a collection of multiple plug-ins). In fact, Babel can convert any code through custom plug-ins. Next, let’s learn about Babel through an example of “converting es6’sclass to es5”.
webpack environment configuration
Everyone should have configured the babel-core loader and its role It provides the core API of Babel. In fact, our code conversion is implemented through plug-ins. Next, we will implement an es6 class conversion plug-in ourselves without using third-party plug-ins. First perform the following steps to initialize a project:How to write a babel plug-in
The babel plug-in is actually implemented through AST (Abstract Syntax Tree). babel helps us convert js code into AST, then allows us to modify it, and finally convert it into js code. So there are two questions involved: What is the mapping relationship between js code and AST? How to replace or add a new AST? Okay, let’s first introduce a tool: astexplorer.net:This tool can convert a piece of code into AST: As shown in the figure , we wrote an es6 class, and then the right side of the webpage generated an AST for us, which actually turned each line of code into an object, so that we implemented a mapping. Introducing another document: babel-types:This is the API document for creating AST nodes. For example, if we want to create a class, we first convert it in astexplorer.net and find that the AST type corresponding to the class isClassDeclaration. Okay, let’s search in the documentation and find that just calling the following api is enough:
Outputclass corresponding The AST node is
ClassDeclaration
ClassDeclaration in the vistor, which means I want to capture all
in the js code ClassDeclaration Node
module.exports = function ({ types: t }) { return { visitor: { ClassDeclaration(path) { //在这里完成转换 } } }; }
{ types:t} The thing is to deconstruct the
variablet from the parameters. It is actually t in the babel-types document (red box in the picture below), which is used to create nodes:
第二个参数 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 ? item.params[0].name : [] let newConstructorParams = t.identifier(constructorParams) let constructorBody = classInner[i].body constructorFn = t.functionDeclaration(newConstructorId, [newConstructorParams], constructorBody, false, false) } //处理其余不是constructor的节点 else { let protoTypeObj = t.memberExpression(t.identifier(className), t.identifier('prototype'), false) let left = t.memberExpression(protoTypeObj, t.identifier(item.key.name), false) //定义等号右边 let prototypeParams = classInner[i].params.length ? classInner[i].params[i].name : [] let newPrototypeParams = t.identifier(prototypeParams) let prototypeBody = classInner[i].body let right = t.functionExpression(null, [newPrototypeParams], prototypeBody, false, false) let protoTypeExpression = t.assignmentExpression("=", 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 = <a href="//m.sbmmt.com/wiki/60.html" target="_blank">Object</a>.create(Parent)
,当然别忘了处理 super
关键字。
打包后代码
运行 npm start
打包后,我们看到打包后的文件里 class
语法已经成功转换为一个个的es5函数。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
The above is the detailed content of Babel conversion es6 method implementation. For more information, please follow other related articles on the PHP Chinese website!