babel学习系列4-polyfill和transform-runtime的差异

第1篇 Babel学习系列1-Babel历史javascript

第2篇 Babel学习系列2-Babel设计组成java

第3篇 Babel学习系列3-babel-preset,babel-plugingit

这篇主要讲 polyfillruntime 总结下, 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 ,Promiseincludes 都没有转换 ,这是为何浏览器

BabelJavascript 语法 分为 syntaxapibash

先说 api , api 指那些咱们能够经过 函数从新覆盖的语法 ,相似 includes,map,includes,Promise,凡是咱们能想到重写的均可以归属到 apibabel

啥子是 syntax ,像 箭头函数,let,const,class, 依赖注入 Decorators,等等这些,咱们在 Javascript 在运行是没法重写的,想象下,在不支持的浏览器里无论怎么样,你都用不了 let 这个关键字

千万要get到上面这2个点,很是重要,不少人觉得只要 引用了 Babel 就不会出现兼容性问题了,这个是大错特错的

syntax 这个关键字 Babel 的官网只是一笔带过,直译又不许确,网上不少文章在说 polyfilltransform-runtime 的差异都没说到点上,还互相瞎鸡儿抄,这个点上小编仍是很自信的,按照本身的理解,说出两者的差异(默默的给本身加个鸡腿)

Babel 只负责 转换 syntax , includes,map,includes 这些 API 层面的 怎么办, Babel 把这个放在了 单独放在了 polyfill 这个模块处理

Babel 这个设计很是好, 把 Javascript 语法抽象成2个方面的, syntaxpolyfill 独立开来,分而治理,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-envuseBuiltIns 属性了,不了解 @babel/preset-env 看下上篇 useBuiltInsfalse,entry,usage 三个属性

先执行下

npm i @babel/polyfill -s
复制代码

useBuiltIns 设置

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 实现按需加载,通常在项目里使用这样方式

runtime 机制,为组件开发而生

好,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 体积太大,因此经过 presetuseBuiltIns 来实现按需加载,再接着为了知足 npm 组件开发的须要 出现了 @babel/runtime 来作隔离

基本上这篇是大部分人在开发中常遇到,但愿读者都能掌握,看到这眼睛离开手机,会议下整篇文章。

你们能够看到我写的东西都不是按照网上的 直接上代码,说明某个语法是干吗的,那样很是好写,可是缺少生命, 看了就过了,很容易忘记

站在做者的角度思考为何这样实现,感受是一种隔着屏幕和网线那端做者对话,整个知识是流动了,不用强行记忆,就能记住某个配置的具体含义。推荐你们在学习新的东西时候也能够用这种方法,开始会慢些,可是基本不会忘记。

demo地址

相关文章
相关标签/搜索