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地址

相關文章
相關標籤/搜索