-
Notifications
You must be signed in to change notification settings - Fork 136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
babel到底将代码转换成什么鸟样? #9
Comments
比对beble的转换结果,babel似乎差很多。 |
要直接转es5/3的话, ts比babel实用多了 |
谢谢总结! 👍 |
很实用的参考 |
好吧。。。 |
不错的总结! |
收藏 |
感觉和这个相似的文章应该在 CoffeeScript 风靡起来的时候也出现过,毕竟 CoffeeScript 编译出来的也是这样子。 |
不过大部分情况下编译后的 ES5 代码直接就混淆压缩投入使用了,也不会直接阅读编译后的代码。 |
感谢分享。 |
@mishe 请教一下,beble是什么东西?没有搜到这个东西 |
@Plortinus 拼写错误,是buble 参考:https://gitlab.com/Rich-Harris/buble |
mark |
中英文求加空格 |
mark |
赞👍 |
mark |
还是没懂怎么保证const不能在赋值的,编译时怎么实现的? |
好文 |
就是直接编译不过去 |
原文链接
前言
将babel捧作前端一个划时代的工具一定也不为过,它的出现让许多程序员幸福地用上了es6新语法。但你就这么放心地让babel跑在外网?反正我是不放心,我就曾经过被坑过,于是萌生了研究babel代码转换的想法。本文不是分析babel源码,仅仅是看看babel转换的最终产物。
es6在babel中又称为es2015。由于es2015语法众多,本文仅挑选了较为常用的一些语法点,而且主要是分析babel-preset-2015这个插件(react开发的时候,常在webpack中用到这个preset)。
babel-preset-2015
打开babel-preset2015插件一看,一共20个插件。熟悉es2015语法的同志一看,多多少少能从字面意思知道某个插件是用于哪种语法的转换
var, const and let
const和let现在一律转换成var。那const到底如何保证不变呢?如果你在源码中第二次修改const常量的值,babel编译会直接报错。
转换前
转换后:
那let的块级作用怎么体现呢?来看看下面例子,实质就是在块级作用改变一下变量名,使之与外层不同。
转换前:
转换后:
赋值解构
写react的时候,我们使用负值解构去取对象的值,用起来非常爽,像这样:
我们来看看转换的结果:
至于数组呢?如果是一个匿名数组,则babel会帮你先定义一个变量存放这个数组,然后再对需要赋值的变量进行赋值。
转换前:
转换后:
看到这个,感觉转换结果跟我们想的还蛮一致。哈哈,使用的噩梦还没开始。
如果使用匿名对象直接进行赋值解构会怎样呢?如下。babel为了使接收的变量唯一,直接就将匿名对象里的属性拼在一起,组成接收这个匿名对象的变量,吓得我赶紧检查一下项目里有没有这种写法。
转换前:
转换后:
还有一种对象深层次的解构赋值:
转换前:
转换后:
babel在代码顶部生产了一个公共的代码_slicedToArray。大概就是将对象里面的一些属性转换成数组,方便解构赋值的进行。但Symbol.iterator的兼容性并不好(如下图),还是谨慎使用为妙。
另外,下面这种对字符串进行赋值解构也同样使用到_slicedToArray方法:
函数参数默认值及扩展运算符
在es5的年代,一般我们写参数的默认值都会这么写:
我们来看看babel的转换办法:
babel这里使有了arguments来做判。第一种情况涉及解构赋值,因此x和y的值还是有可能是undefined的。至于第二种情况,则会保证2个参数的默认值分别是1和2.
再来看一种。...y代表它接收了剩下的参数。也就是arguments除了第一个标号的参数之外剩余的参数。
转换前:
转换后:
箭头函数
剪头函数其实主要是省了写函数的代码,同时能够直接用使外层的this而不用担心context切换的问题。以前我们一般都要在外层多写一个_this/self直向this。babel的转换办法其实跟我们的处理无异。
转换前:
转换后:
对象的能力增强
对象属性的快捷定义
转换前:
转换后:
对象中括号属性
es2015开始新增了在对象中用中括号解释属性的功能,这对变量、常量等当对象属性尤其有用。
转换前:
转换后:
看似简单的属性,babel却大动干戈。新增了一个_defineProperty函数,给新建的_obj = {}进行属性定义。除此之外使用小括号包住一系列从左到右的运算使整个定义更简洁。
使用super去调用prototype
以前我们一般都用obj.prototype或者尝试用this去往上寻找prototype上面的方法。而babel则自己写了一套在prototype链上寻找方法/属性的算法。
转换前
转换后:
Object.assign 和 Object.is
es6新增的Object.assign极大方便了对象的克隆复制。但babel的es2015 preset并不支持,所以没对其进入转换,这会使得一些移动端机子遇到这种写法会报错。所以一般开发者都会使用object-assign这个npm的库做兼容。
Object.is用于比较对象的值与类型,es2015 preset同样不支持编译。
es6模板
多行字符串
转换前:
转换后:
字符中变量运算
转换前:
转换后:
标签模板
es6的这种新特性给模板处理赋予更强大的功能,一改以往对模板进行各种replace的处理办法,用一个统一的handler去处理。babel的转换主要是添加了2个属性,因此看起来也并不算比较工程浩大的编译。
转换前:
转换后:
模块化与类
类class
javascript实现oo一直是非常热门的话题。从最原始时代需要手动维护在构造函数里调用父类构造函数,到后来封装好函数进行extend继承,再到babel出现之后可以像其它面向对象的语言一样直接写class。es2015的类方案仍然算是过渡方案,它所支持的特性仍然没有涵盖类的所有特性。目前主要支持的有:
转换前:
转换后(由于代码太长,先省略辅助的方法):
先前在用react重构项目的时候,所有的react组件都已经摒弃了es5的写法,一律采用了es6。用类的好处写继续更加方便,但无法用mixin,需要借助更新的es7语法中的decorator才能够实现类mixin的功能(例如pureRender)。但这次分析完babel源码之后,才发现原来babel在实现class特性的时候,定义了许多方法,尽管看起来并不太优雅。
模块化
在开发react的时候,我们往往用webpack搭配babel的es2015和react两个preset进行构建。之前看了一篇文章对babel此处的模块加载有些启发(《分析 Babel 转换 ES6 module 的原理》)。
示例:
通过webpack与babel编译后:
es6的模块加载是属于多对象多加载,而commonjs则属于单对象单加载。babel需要做一些手脚才能将es6的模块写法写成commonjs的写法。主要是通过定义__esModule这个属性来判断这个模块是否经过babel的编译。然后通过_interopRequireWildcard对各个模块的引用进行相应的处理。
另一个发现是,通过webpack打包babel编译后的代码,每一个模块里面都包含了相同的类继承帮助方法,这是开发时忽略的。由此可看,在开发react的时候用es5的语法可能会比使用es6的class能使js bundle更小。
babel es2015 loose mode
开发家校群的时候,在android4.0下面报esModule错误的问题,如下:
经查证,发现是构建中babel-es2015 loader的模式问题,会导致Android4.0的用户有报错。只需要使用loose mode就可以解决问题。下面是相关的stackoverflow issue以及对应解决问题的npm包。
那么es2015和normal mode和loose mode有什么区别呢,这个出名的博客略有介绍:Babel 6: loose mode。
实质就是(作者总结)normal mode的转换更贴近es6的写法,许多的property都是通过Object.defineProperty进行的。而loose mode则更贴近es5的写法,性能更好一些,兼容性更好一些,但将这部份代码再转换成native es6的话会比较麻烦一些(感觉这一点并不是缺点,有源码就可以了)。
上面esModule解决的办法,实质就是将
改成
exports.__esModule = true;
再举个例子,如下面的Cat类定义:
正常模式会编译为:
loose mode模式会编译为:
babel es2015中loose模式主要是针对下面几个plugin:
每一种的转换方式在此就不再赘述了,大家可以回家自己试。
如有错误,恳请斧正!
参考文章:
babel try out
template literals
block-scoped functions
classes
objects
commonjs and es6 module
The text was updated successfully, but these errors were encountered: