第1篇 Babel学习系列1-Babel历史javascript
第2篇 Babel学习系列2-Babel设计组成java
第3篇 Babel学习系列3-babel-preset,babel-plugingit
这篇主要讲 polyfill
和 runtime
总结下, Babel
只是转换 syntax
层语法,全部须要 @babel/polyfill
来处理API兼容,又由于 polyfill
体积太大,因此经过 preset
的 useBuiltIns 来实现按需加载,再接着为了知足 npm 组件开发的须要 出现了 @babel/runtime
来作隔离github
下面上一段 常见代码npm
转换前代码api
let array = [1, 2, 3, 4, 5, 6];
array.includes(item => item > 2);
new Promise()
复制代码
Babel转换后代码数组
var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
return item > 2;
});
new Promise()
复制代码
Babel
默认只是转换了 箭头函数 let
,Promise
和 includes
都没有转换 ,这是为何浏览器
Babel
把 Javascript
语法 分为 syntax
和 api
bash
先说 api
, api
指那些咱们能够经过 函数从新覆盖的语法 ,相似 includes,map,includes,Promise
,凡是咱们能想到重写的均可以归属到 api
babel
啥子是 syntax
,像 箭头函数,let,const,class, 依赖注入 Decorators
,等等这些,咱们在 Javascript
在运行是没法重写的,想象下,在不支持的浏览器里无论怎么样,你都用不了 let 这个关键字
千万要get到上面这2个点,很是重要,不少人觉得只要 引用了 Babel
就不会出现兼容性问题了,这个是大错特错的
syntax
这个关键字 Babel
的官网只是一笔带过,直译又不许确,网上不少文章在说 polyfill
和 transform-runtime
的差异都没说到点上,还互相瞎鸡儿抄,这个点上小编仍是很自信的,按照本身的理解,说出两者的差异(默默的给本身加个鸡腿)
那 Babel
只负责 转换 syntax
, includes,map,includes
这些 API
层面的 怎么办, Babel
把这个放在了 单独放在了 polyfill
这个模块处理
Babel
这个设计很是好, 把 Javascript
语法抽象成2个方面的, syntax
和 polyfill
独立开来,分而治理,6to5
一开始设计是把两者放在一块儿的,你们想一想 polyfill
随着浏览器的不一样,差别是很是大的,2个要是在一块儿 代码的耦合性就太大了,处处都是if else
polyfill
直译的话是垫片的意思,那处理相似 assign,includes,map,includes
,某些浏览器 没有的方法 最直接的办法的是 根据 一份浏览器不兼容的表格(这个browserslist
已经完成了),把对应浏览器不支持的语法所有从新写一遍,相似下面这样
//
if (typeof Object.assign != 'function') {
Object.defineProperty(Object, "assign",
·····
}
if (!Array.prototype.includes){
Object.defineProperty(Array.prototype, 'includes',
·····
}
if (!Array.prototype.every){
Object.defineProperty(Array.prototype, 'every',
·····
}
.....好多好多
复制代码
这种方式能够简单粗暴的解决兼容性问题, 那问题也来了,这样会致使 polyfill.js
这个包很是大,这个你们又受不了
怎么办,Babel 开发大佬们确定是又办法的,只要我用到了includes
Babel 就只给我引入 includes
的对应的不就行了,按需加载,要啥给我加载啥
这个就须要用到上篇说到 @babel/preset-env
的 useBuiltIns
属性了,不了解 @babel/preset-env
看下上篇 useBuiltIns
有 false
,entry
,usage
三个属性
先执行下
npm i @babel/polyfill -s
复制代码
false
是默认值,表示啥也不干
entry
表示就是把所有 @babel/polyfill
所有一次性引入
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"debug": true,
"useBuiltIns": "entry",
"targets":{
"browsers":["> 1%"]
}
}
]
]
}
复制代码
而后在 sample.js
中引入 打包后生成的 文件是这样样子的
这个通常没人用,长的丑的人才会用,由于体积实在是太大了
usage
的意思是 按需加载 ,把上面 改为 "useBuiltIns": "usage"
打包出来以下
这就是这 2 个值得差异, 使 用entry
须要手动在 sample.js
中手动 @babel/polyfill
,而且会把全部的 polyfill
引入进来, 使用 usage
实现按需加载,通常在项目里使用这样方式
好,polyfill
问题是已经解决了,如今又出现了一个场景, 假设你是开发 一个 npm 组件的选手 你恰好用到了 Promise
方法, 按照上面的方法,写完发布到 npm 仓库 如今隔壁印度小哥恰好搜到你这包,下载下来了,可是他的项目里面也用到了 Promise
,可是他的 Promise
是 自定义的 一套,相似
window.Promise = function (){
this.reject = ..
this.resolve = ..
}
复制代码
这个时候就傻眼了,小哥项目跑不起来了,跑到 github 上用蹩脚 English 骂了你一通,这个场景其实很常见,那这么办,那假设在开发 组件的时能报把全部的 Promise copy
到 _Promise
对象上,而后组件里都用 _Promise
,是否是就和外界作了层隔离,互不影响,哈哈完美,这个思路咱们在开发设计中也是能够学习套用的,那 Babel
里面针对 这种场景 出现了@babel/runtime
, @babel/plugin-transform-runtime
首先 执行下,这2个一个都不能少,都是必须的
// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"targets":{
"browsers":["> 1%", "last 2 versions", "not ie <= 8"]
}
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2 // 参考官方文档
}
]
],
}
npm install --save @babel/runtime
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs2 --save // 官方文档 说这个能够不加,我试了不加,没起做用
复制代码
上面代码 会转换成以下
能够看到 Promise, Array.isArray,Object.assign
,都对应的转换了 可是 图片里的 Array.prototype.includes
原型方法是没有转换的(网上文章讲到这都是一笔带过,压根没讲为何会这样设计),为啥 Babel
的做者为何这样设计,实际上是由于他无能为力,要实现 这个功能 须要在 把全部数组包裹起来,例以下面这样
let arry = [1,2,3,4] 转换成
let array = function(){
return new _Array(1,2,3,4) // 假设是这个样子
}
复制代码
可是 Javascript
是个弱类型语言,在 AST
层面没法解析到判断某个变量是否是一个数组,因此很无奈, runtime
也不是包治百病,须要配合 @babel/polyfill
配合转换 原型链上的方法
OK,可算写完了,总结下, Babel
只是转换 syntax
层语法,全部须要 @babel/polyfill
来处理API兼容,又由于 polyfill
体积太大,因此经过 preset
的 useBuiltIns
来实现按需加载,再接着为了知足 npm 组件开发的须要 出现了 @babel/runtime
来作隔离
基本上这篇是大部分人在开发中常遇到,但愿读者都能掌握,看到这眼睛离开手机,会议下整篇文章。
你们能够看到我写的东西都不是按照网上的 直接上代码,说明某个语法是干吗的,那样很是好写,可是缺少生命, 看了就过了,很容易忘记
站在做者的角度思考为何这样实现,感受是一种隔着屏幕和网线那端做者对话,整个知识是流动了,不用强行记忆,就能记住某个配置的具体含义。推荐你们在学习新的东西时候也能够用这种方法,开始会慢些,可是基本不会忘记。