0

0

babel转换es6步奏详解

php中世界最好的语言

php中世界最好的语言

发布时间:2018-04-20 11:23:12

|

1930人浏览过

|

来源于php中文网

原创

这次给大家带来babel转换es6步奏详解,babel转换es6的注意事项有哪些,下面就是实战案例,一起来看一下。

babel是一个转码器,目前开发react、vue项目都要使用到它。它可以把es6+的语法转换为es5,也可以转换JSX等语法。

我们在项目中都是通过配置插件和预设(多个插件的集合)来转换特定代码,例如env、stage-0等。

实际上babel可以通过自定义插件的方式实现任何代码的转换,接下来我们通过一个“把es6的 class 转换为es5”的例子来了解一下babel。

内容如下:

webpack环境配置

大家应该都配置过babel-core这个loader,它的作用是提供babel的核心Api,实际上我们的代码转换都是通过插件来实现的。

接下来我们不用第三方的插件,自己实现一个es6类转换插件。先执行以下几步初始化一个项目:

  1. npm install webpack webpack-cli babel-core -D

  2. 新建一个webpack.config.js

  3. 配置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就可以了:

创建其他语句也是一样的道理,有了上面这两个东西,我们可以做任何转换了。

下面我们开始真正编写一个插件,分为以下几步:

  1. 在index.js中export一个函数

  2. 函数中返回一个对象,对象有一个visitor参数(必须叫visitor)

  3. 通过astexplorer.net查询class 对应的AST节点为 ClassDeclaration

  4. 在vistor中设置一个捕获函数 ClassDeclaration ,意思是我要捕获js代码中所有 ClassDeclaration 节点

  5. 编写逻辑代码,完成转换

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() 方法,这需要我们做兼容处理。

接下来我们开始写代码,思路是:

  1. 拿到老的AST节点

  2. 创建一个数组用来盛放新的AST节点(虽然原class只是一个节点,但是替换后它会被若干个函数节点取代) 初始化默认的 constructor 节点(上文提到,class中有可能没有定义constructor)

  3. 循环老节点的AST对象(会循环出若干个函数节点)

  4. 判断函数的类型是不是 constructor ,如果是,通过取到数据创建一个普通函数节点,并更新默认 constructor 节点

  5. 处理其余不是 constructor 的节点,通过数据创建 prototype 类型的函数,并放到 es5Fns

  6. 循环结束,把 constructor 节点也放到 es5Fns

  7. 判断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 = Object.create(Parent) ,当然别忘了处理 super 关键字。

打包后代码

 

运行 npm start 打包后,我们看到打包后的文件里 class

语法已经成功转换为一个个的es5函数。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

vue cli如何升级webapck4

uBrand
uBrand

一站式AI品牌创建平台,在线品牌设计,AI品牌策划,智能品牌营销;uBrand帮助创业者轻松打造个性品牌!

下载

AngularJS应用模块化的详细步奏

vue-cli 3.0 新手入门必知

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

2

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

1

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

76

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

73

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

67

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

19

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Javascript - ES6实战视频课程
Javascript - ES6实战视频课程

共34课时 | 9.9万人学习

Go 教程
Go 教程

共32课时 | 4.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号