第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
來作隔離
基本上這篇是大部分人在開發中常遇到,但願讀者都能掌握,看到這眼睛離開手機,會議下整篇文章。
你們能夠看到我寫的東西都不是按照網上的 直接上代碼,說明某個語法是幹嗎的,那樣很是好寫,可是缺少生命, 看了就過了,很容易忘記
站在做者的角度思考爲何這樣實現,感受是一種隔着屏幕和網線那端做者對話,整個知識是流動了,不用強行記憶,就能記住某個配置的具體含義。推薦你們在學習新的東西時候也能夠用這種方法,開始會慢些,可是基本不會忘記。