1. bebel該如何使用
第一步 安裝
npm install babel-cli --save-dev
或 cnpm install babel-cli --save-dev
如果想設定轉碼規則使用
cnpm install babel-preset-es2015 --save-dev
會在package.json中生成包如下(部分)
"babel-core": "^6.22.1",//babel轉譯器本身,提供了babel的轉譯API,如babel.transform等,用於對程式碼進行轉譯。像webpack的babel-loader就是呼叫這些API來完成轉譯過程的。
"babel-plugin-transform-runtime": "^6.22.0",//babel轉譯過程中使用到的外掛,其中babel-plugin-transform-xxx是transform步驟使用的
"babel-preset-env": "^1.3.2",//transform階段使用到的一系列的plugin
"babel-preset-stage-2": "^6.22.0",//transform階段使用到的一系列的plugin
"babel-register": "^6.22.0",//通過繫結node.js的require來自動轉譯require引用的js程式碼檔案
複製程式碼
(不建議安裝在全域性,因為不同專案可能會使用不同的bebel版本)
第二步 建立配置檔案 .babelrc (放在根目錄下,不建議在package.json裡在babel欄位新增設定)
{
"presets": [//要載入和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按從尾到頭的逆序執行(為了相容使用者使用習慣)
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
//"es2015"
],
"plugins": ["transform-runtime"],//該屬性是告訴babel要使用那些外掛,這些外掛可以控制如何轉換程式碼。要載入和使用的外掛列表,外掛名前的babel-plugin-可省略;plugin列表按從頭到尾的順序執行
"env": {//指定在不同環境下使用的配置。比如production和development兩個環境使用不同的配置,就可以通過這個欄位來配置。env欄位的從process.env.BABEL_ENV獲取,如果BABEL_ENV不存在,則從process.env.NODE_ENV獲取,如果NODE_ENV還是不存在,則取預設值"development"
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
//同時設定了presets和plugins,那麼plugins的先執行;
複製程式碼
babel會從當前轉譯的檔案所在目錄下查詢配置檔案,如果沒有找到,就順著文件目錄樹一層層往上查詢,一直到.babelrc檔案存在或者帶babel欄位的package.json檔案存在為止。
第三步 webpack.base.conf.js中配置babel
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
}
]
}
複製程式碼
第四步 在package.json中配置專案的瀏覽器相容情況
(具體引數參照[github.com/browserslis…])
"browserslist": [
"> 1%",
"last 2 versions",
"Android >= 3.2",
"Firefox >= 20",
"iOS 7"
]
複製程式碼
作用:
- 【.babelrc】檔案可以針對配置
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions"],
"node": "current"
},
}]
]
}
複製程式碼
- 根據瀏覽器可以獲得特性,比如最新的chrome瀏覽器支援原生的promise,而IE不支援,babel根據browserslist配置項就會動態的轉義。不用在一個個進行配置了。
第五步 進入根目錄,npm run build執行,獲得打包檔案
2. babel的工作原理
babel是一個轉譯器,它把同種語言的高版本規則翻譯成低版本規則。
babel的轉譯過程分為三個階段:parsing、transforming、generating,以ES6程式碼轉譯為ES5程式碼為例,babel轉譯的具體過程如下:
ES6程式碼輸入 -> babylon進行解析 -> 得到AST -> plugin用babel-traverse對AST樹進行遍歷轉譯 -> 得到新的AST樹
-> 用babel-generator通過AST樹生成ES5程式碼
複製程式碼
【!!!注意!!!】
Babel預設只轉換新的javascript語法,而不轉換新的API,比如 Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise 等全域性物件。以及一些在全域性物件上的方法(比如 Object.assign)都不會轉碼。
比如說,ES6在Array物件上新增了Array.form方法,Babel就不會轉碼這個方法,如果想讓這個方法執行,必須使用 babel-polyfill
來轉換等。
babel-polyfill
和babel-runtime
就是為了解決新的API與這種全域性物件或全域性物件方法不足的問題,因此可以使用這兩個外掛可以轉換的。
3.babel-polyfill 使用方法
- 先安裝包:
npm install --save babel-polyfill
- 在入口處匯入polyfill,保證polyfill程式碼在所有其他程式碼前先被呼叫
import "babel-polyfill"
- webpack配置:
module.exports = { entry: ["babel-polyfill", "./app/js"] };
如果只是需要引入部分新原生物件或API,那麼可以按需引入,而不必匯入全部的環境,如下:
【通過core-js實現按需引入polyfill或runtime】
- core-js是polyfill、runtime的核心,因為polyfill和runtime其實都只是對core-js和regenerator的再封裝,方便使用而已。
- polyfill和runtime都是整體引入的,不能做細粒度的調整,如果我們的程式碼只是用到了小部分ES6而導致需要使用polyfill和runtime的話,會造成程式碼體積不必要的增大(runtime的影響較小)。所以,按需引入的需求就自然而然產生了,這個時候就得依靠core-js來實現了。
- core-js有三種使用方式
-
預設方式:
require('core-js')
:這種方式包括全部特性,標準的和非標準的,類似polyfill。require('core-js/fn/set'); require('core-js/fn/array/from'); require('core-js/fn/array/find-index'); Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] [1, 2, NaN, 3, 4].findIndex(isNaN); // => 2 複製程式碼
-
庫的形式:
var core = require('core-js/library')
:這種方式也包括全部特性,類似runtime,只是它不會汙染全域性名字空間,但是不能使用例項方法。var Set = require('core-js/library/fn/set'); var from = require('core-js/library/fn/array/from'); var findIndex = require('core-js/library/fn/array/find-index'); from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] findIndex([1, 2, NaN, 3, 4], isNaN); // => 2 複製程式碼
想要使用prototype方法:通過::這個符號而不是.來呼叫例項方式,從而達到曲線救國的目的。這種方式的使用,路徑中都會帶有/virtual/
import {fill, findIndex} from 'core-js/library/fn/array/virtual'; Array(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4 // 對比下polyfill的實現 // Array(10).fill(0).map((a, b) => b * b).findIndex(it => it && !(it % 8)); 複製程式碼
-
shim:
require('core-js/shim')
或var shim = require('core-js/library/shim')
: 這種方式只包括標準特性(就是隻有polyfill功能,沒有擴充套件的特性)舉例說明
-
core-js/es6(core-js/library/es6)
包含了全部的ES6特性, -
core-js/es6/array(core-js/library/es6/array)
只包含ES6的Array特性 -
core-js/fn/array/from(core-js/library/fn/array/from)
只有Array.from這個實現(具體的每個特性和對應的路徑可以參照[github.com/zloirock/co…])
-
4.babel-polyfill和babel-runtime的區別
babel-polyfill
會做相容執行環境中並沒有實現的一些方法。babel-runtime
是將es6編譯成es5去執行。使用es6的語法編寫,最終會通過babel-runtime
編譯成es5。也就是說,不管瀏覽器是否支援ES6,只要是ES6的語法,它都會進行轉碼成ES5。所以就有很多冗餘的程式碼。babel-polyfill
是通過向全域性物件和內建物件的prototype上新增方法來實現的。比如執行環境中不支援Array.prototype.find 方法,引入polyfill, 我們就可以使用es6方法來編寫了,但是缺點就是會造成全域性空間汙染。babel-runtime
不會汙染全域性物件和內建物件的原型,比如說我們需要Promise,我們只需要import Promise from 'babel-runtime/core-js/promise'
即可,這樣不僅避免汙染全域性物件,而且可以減少不必要的程式碼。
【綜上】雖然 babel-runtime
可以解決 babel-polyfill
中的避免汙染全域性物件,但是它自己也有缺點的,比如我現在有100個檔案甚至更多的話,就需要一個個檔案加import Promise from 'babel-runtime/core-js/promise'
,為解決這一問題,引入了babel-plugin-transform-runtime
。避免手動引入多個檔案的import,並且它還做了公用方法的抽離。比如說有100個模組都使用promise,但是promise的polyfill僅僅存在1份。
這就是babel-plugin-transform-runtime
外掛的作用。
5.transform-runtime和babel-runtime的區別
babel-plugin-transform-runtime外掛依賴babel-runtime,babel-runtime是真正提供runtime環境的包;也就是說transform-runtime外掛是把js程式碼中使用到的新原生物件和靜態方法轉換成對runtime實現包的引用,舉個例子如下:
// 輸入的ES6程式碼
var sym = Symbol();
// 通過transform-runtime轉換後的ES5+runtime程式碼
var _symbol = require("babel-runtime/core-js/symbol");
var sym = (0, _symbol.default)();
複製程式碼
原本程式碼中使用的ES6新原生物件Symbol被transform-runtime外掛轉換成了babel-runtime的實現,既保持了Symbol的功能,同時又沒有像polyfill那樣汙染全域性環境(因為最終生成的程式碼中,並沒有對Symbol的引用)
而且babel-runtime其實也不是真正的實現程式碼所在,真正的程式碼實現是在core-js中。
6.transform-runtime外掛的功能
- 把程式碼中的使用到的ES6引入的新原生物件和靜態方法用babel-runtime/core-js匯出的物件和方法替代
- 當使用generators或async函式時,用babel-runtime/regenerator匯出的函式取代(類似polyfill分成regenerator和core-js兩個部分)
- 把Babel生成的輔助函式改為用babel-runtime/helpers匯出的函式來替代(babel預設會在每個檔案頂部放置所需要的輔助函式,如果檔案多的話,這些輔助函式就在每個檔案中都重複了,通過引用babel-runtime/helpers就可以統一起來,減少程式碼體積)
- babel-plugin-transform-runtime 的配置一些選項
'plugins': [
[
'transform-runtime',
{
'helpers': false,//預設值為true,表示是否開啟內聯的babel helpers(即babel或者環境本來存在的某些物件方法函式)如:extends,etc這樣的在呼叫模組名字時將被替換名字。
'polyfill': false,//預設值為true,表示是否把內建的東西(Promise, Set, Map)等轉換成非全域性汙染的。
'regenerator': true,//預設值為true,是否開啟generator函式轉換成使用regenerator runtime來避免汙染全域性域。
'moduleName': 'flavortown/runtime'//預設值為 babel-runtime,引入規則:import extends from 'flavortown/runtime/helpers/extends';
}
]
]
}
複製程式碼
【綜上】:babel-runtime就是一個提供了regenerator、core-js和helpers的執行時庫。transform-runtime在.babelrc裡配置的時候,還可以設定helpers、polyfill、regenerator這三個開關,以自行決定runtime是否要引入對應的功能。
【注意1】:建議不要直接使用babel-runtime,因為transform-runtime依賴babel-runtime,大部分情況下都可以用transform-runtime達成目的。
【注意2】:由於runtime不會汙染全域性空間,所以例項方法是無法工作的(因為這必須在原型鏈上新增這個方法,這是和polyfill最大的不同) ,比如:
var arr = ['a', 'b', 'c'];
arr.fill(7); // 例項方法不行
Array.prototype.fill.apply(arr, 7); // 用原型鏈來呼叫也是不行
複製程式碼