一.簡介:
babel官網:https://www.babeljs.cn/
githup:https://github.com/babel/babel
Babel 是一個工具鏈,主要用於將 ECMAScript 2015+ 版本的代碼轉換爲向後兼容的 JavaScript 語法,以便能夠運行在當前和舊版本的瀏覽器或其他環境中。
我這裏只是介紹基本的使用方法,詳細的可以到官網查看。
1.1基本實現的效果:
// Babel 輸入: ES2015 箭頭函數
[1, 2, 3].map((n) => n + 1);
//-------------------------------------------------------------
// Babel 輸出: ES5 語法實現的同等功能
[1, 2, 3].map(function(n) {
return n + 1;
});
1.2 整個設置過程:
運行以下命令以安裝軟件包:
(這些命令是通過的,哪些不滿足請自己安裝)
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save-dev @babel/preset-stage-2
npm install --save @babel/plugin-transform-runtime
npm install --save npm install @babel-plugin-syntax-dynamic-import
.babelrc 文件(根目錄下),vue項目開發使用的配置如下:
{
"presets": [
[ "@babel/preset-env", {
"modules": false
}],
"stage-2"
],
// 下面指的是在生成的文件中,不產生註釋
"comments": false,
"plugins": ["transform-runtime","syntax-dynamic-import"],
// 下面的代碼可以不要
"env": {
// test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
"test": {
"presets": [ "@babel/preset-env", "stage-2"],
// instanbul是一個用來測試轉碼後代碼的工具
"plugins": ["istanbul"]
}
}
}
.babelrc 文件(根目錄下),React項目開發使用的配置如下:
{
"presets": [
[ "@babel/preset-env",, { "modules": false }],
"stage-2",
"react"
]
"plugins": ["transform-runtime"],
"comments": false,
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": [ "istanbul" ]
}
}
}
上面的瀏覽器列表只是一個任意示例。您需要進行調整以適合您要支持的瀏覽器。
1.3 babel7
@ 符號是什麼?這是 babel 7 的一大調整,原來的 babel-xx 包統一遷移到babel 域下 - 域由 @ 符號來標識,一來便於區別官方與非官方的包,二來避免可能的包命名衝突。
二.配置文件.babelrc
主要是對預設(presets
)和插件(plugins
)進行配置,基本格式:
{
"presets": [],
"plugins": []
}
2.1babel轉譯器
不同的轉譯器作用不同的配置項,大致可分爲以下三種:
2.1.2 語法轉義器
主要對javascript
最新的語法糖進行編譯,並不負責轉譯javascript
新增的api
和全局對象。例如let/const
就可以被編譯,而String.includes
/Object.assign
等並不能被編譯。常用到的轉譯器包有,babel-preset-env
、babel-preset-es2015
、babel-preset-es2016
、babel-preset-es2017
、babel-preset-latest
等。在實際開發中可以只選用babel-preset-env來代替餘下的,但是還需要配上javascirpt的製作規範一起使用,同時也是官方推薦
{
"presets":
[
"env",
{
"modules": false
}
],
"stage-2"
}
2.1.2 補丁轉義器
主要負責轉譯javascript
新增的api
和全局對象,例如babel-plugin-transform-runtime
這個插件能夠編譯Object.assign
,同時也可以引入babel-polyfill
進一步對includes
這類用法保證在瀏覽器的兼容性。Object.assign
會被編譯成以下代碼:
__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_object_assign___default()
2.1.3 jsx和flow插件
這類轉譯器用來轉譯JSX語法和移除類型聲明的,使用Rect的時候你將用到它,轉譯器名稱爲babel-preset-react
2.2預設(Presets)
2.2.1官方針對常用環境編寫了一些 preset
@babel/preset-env
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript
2.2.2 Stage-X (實驗性質的 Presets)
從 Babel v7 開始,所有針對處於標準提案階段的功能所編寫的預設(stage preset)都已被棄用。
stage-x preset 中的任何語法轉換都是對語言本身的更改,而這些更改尚未被納入 JavaScript 標準(例如 ES6/ES2015)
- Stage 0 - 設想(Strawman):只是一個想法,可能有 Babel插件。
- Stage 1 - 建議(Proposal):這是值得跟進的。
- Stage 2 - 草案(Draft):初始規範。
- Stage 3 - 候選(Candidate):完成規範並在瀏覽器上初步實現。
- Stage 4 - 完成(Finished):將添加到下一個年度版本發佈中。
處於 stage-3 之前的任何提案,請務必謹慎使用
2.2.3Preset 的路徑
如果 preset 在 npm 上,你可以輸入 preset 的名稱,babel 將檢查是否已經將其安裝到 node_modules 目錄下了
{
"presets": ["babel-preset-myPreset"]
}
你還可以指定指向 preset 的絕對或相對路徑。
{
"presets": ["./myProject/myPreset"]
}
2.2.4 Preset 的短名稱
如果 preset 名稱的前綴爲 babel-preset-,你還可以使用它的短名稱:
{
"presets": [
//"babel-preset-myPreset"
"myPreset",
]
}
2.2.5 Preset 的排列順序
Preset 是逆序排列的(從後往前)。
{
"presets": [
"a",
"b",
"c"
]
}
將按如下順序執行: c、b 然後是 a。
這主要是爲了確保向後兼容,由於大多數用戶將 “es2015” 放在 “stage-0” 之前。
2.2.6 Preset 的參數
插件和 preset 都可以接受參數,參數由插件名和參數對象組成一個數組,可以在配置文件中設置。
如果不指定參數,下面這幾種形式都是一樣的:
{
"presets": [
"presetA",
["presetA"],
["presetA", {}],
]
}
要指定參數,請傳遞一個以參數名作爲鍵(key)的對象。
{
"presets": [
["@babel/preset-env", {
"loose": true,
"modules": false
}]
]
}
2.3 插件(plugins)
官方文檔
Babel 是一個編譯器(輸入源碼 => 輸出編譯後的代碼)。就像其他編譯器一樣,編譯過程分爲三個階段:解析、轉換和打印輸出。
除了一個一個的添加插件,你還可以以 preset 的形式啓用一組插件。
2.4 插件和預設選項
官方文檔
官方推薦使用babel-preset-env來替代一些插件包的安裝(es2015-arrow-functions,es2015-block-scoped-functions等等),並且有如下幾種配置信息,介紹幾個常用的
2.4.1 targets
.babelrc
"presets": ["@babel/preset-env", {
{
"targets": {
"chrome": 52,
"browsers": ["last 2 versions", "safari 7"],
"node":"6.10"
}
"modules": false
}
}]
targets可以制定兼容瀏覽器版本,如果設置了browsers,那麼就會覆蓋targets原本對瀏覽器的限制配置。
targets.node對node某一個版本進行編譯
modules通常都會設置爲false,因爲默認都是支持CommonJS規範
2.4.2 ignore
主要作用就是可以指定不編譯哪些代碼
.babelrc
{
"ignore":["./module/a.js"]
}
2.4.2 minified
主要設置編譯後是否是壓縮,boolean類型,如果使用babel-cli進行打包編譯文件這個配置項能夠起到作用,但是目前大部分還是會依賴第三方打包工具,例如webpack,所以這個配置參數一般不用設置,webpack插件中的UglifyJsPlugin做了壓縮的工作。
2.4.2 comments
在生成的文件中,不產生註釋,boolean類型,webpack插件中的UglifyJsPlugin也同樣集成了這個功能。
2.4.3 env
官網
.babelrc
{
"env": {
// test 是提前設置的環境變量,如果沒有設置BABEL_ENV則使用NODE_ENV,如果都沒有設置默認就是development
"test": {
"presets": ["env", "stage-2"],
// instanbul是一個用來測試轉碼後代碼的工具
"plugins": ["istanbul"]
}
}
}
3. 常用的
babel7
3.1 @babel-core
babel代碼的核心
npm install --save-dev @babel/core
3.2 @babel/cli
babel 提供的命令行工具,用於命令行下編譯源代碼
npm install --save-dev @babel/cli
更改packaeg.json
"scripts": {
// 編譯src文件夾下的index.js,並導出到lib文件夾
"build": "babel src -d lib"
},
運行npm run build
,
發現的確是生成了lib文件夾,但是lib/index.js還是原來的代碼,沒有任何變化。說好的編譯呢?
這個調整則是在 babel 6 裏發生的。Babel 6 做了大量模塊化的工作,將原來集成一體的各種編譯功能分離出去,獨立成插件。這意味着,默認情況下,當下版本的 babel 不會編譯代碼。
3.3 @babel/plugin-transform-arrow-functions
箭頭函數編譯成 ES5 函數,需要安裝額外的 babel 插件
npm install --save-dev @babel/plugin-transform-arrow-functions
使用.babelrc:
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
3.4 @babel/preset-env
可以把 Preset 理解爲套餐,每個套餐裏打包了不同的插件,這樣安裝套餐就等於一次性安裝各類 babel 插件。
$ npm install --save-dev @babel/preset-env
.babelrc:
{
"presets": ["@babel/preset-env"]
}
3.5 @babel/polyfill
Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼。
舉例來說,ES6在Array對象上新增了Array.from方法。Babel就不會轉碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,爲當前環境提供一個墊片。
babel-polyfill - 讓目標瀏覽器支持所有特性,不管它是全局的,還是原型的,或是其它。這樣,通過 babel-polyfill,不同瀏覽器在特性支持上就站到同一起跑線。
npm install --save @babel/polyfill
使用 babel-polyfill
在程序入口文件的頂部引用 @babel-polyfill:
require('@babel/polyfill')
或者
import '@babel/polyfill'
需要注意的是,babel-polyfill 不能多次引用。如果我們的代碼中有多個 require(’@babel/polyfill’),則執行時會報告錯誤:
only one instance of @babel/polyfill is allowed
這是因爲引入的 babel-polyfill 會在全局寫入一個 _babelPolyfill 變量。第二次引入時,會檢測該變量是否已存在,如果已存在,則拋出錯誤。
注意事項
如前面所說的,babel-polyfill 其實包含 regenerator runtime、core-js,如果你的代碼只需要其中一部分 polyfill,那麼你可以考慮直接引入 core-js 下的特定 polyfill,不必使用 babel-polyfill 這樣的龐然大物。
另一種辦法,是配合 @babel/preset-env 的 useBuiltIns 配置。
3.6 babel-runtime
3.6.1abel-runtime
與 babel-polyfill
的區別究竟是什麼?
babel-polyfill
與 babel-runtime
的一大區別,前者改造目標瀏覽器,讓你的瀏覽器擁有本來不支持的特性;後者改造你的代碼,讓你的代碼能在所有目標瀏覽器上運行,但不改造瀏覽器。
例如:
引入 babel-polyfill 後的 IE 11,你可以在 console 下執行 Object.assign({}, {})。引入 babel-runtime 後的 IE 11,console 下執行 Object.assign({}, {})仍然提示報錯
我們拿 Object.assign
爲例,剖析下 babel-polyfill
與 babel-runtime
的異同。
我們知道,IE 11 不支持 Object.assign
,此時,我們有倆種候選方案:
- 引入
babel-polyfill
,補丁一打,Object.assign
就被創造出來 - 配置
@babel/plugin-transform-object-assign
第二種方案中,babel 會將所有的 Object.assign 替換成 _extends 這樣一個輔助函數。如下所示:
Object.assign({}, {})
編譯成:
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
_extends({}, {});
問題是,如果你的項目裏有 100 個文件,其中有 50 個文件裏寫了 Object.assign
,那麼,壞消息來了,_extends
輔助函數會出現 50 次。
怎麼辦?我們自然而然會想到把 _extends
分離出去,然後在每個文件中引入 - 這正是 @babel/runtime
的作用:
var _extends = require("@babel/runtime/helpers/extends");
_extends({}, {});
babel 提供了 @babel/plugin-transform-runtime
來替我們做這些轉換。
3.6.2 使用
$ npm install --save-dev @babel/plugin-transform-runtime
npm install @babel/runtime
.babelrc 中配置:
{
"plugins": [
"@babel/plugin-transform-object-assign",
"@babel/plugin-transform-runtime"
]
}
這樣,我們不需要 babel-polyfill 也一樣可以在程序中使用 Object.assign,編譯後的代碼最終能夠正常運行在 IE 11 下。
3.7@babel/register
經過 babel 的編譯後,我們的源代碼與運行在生產下的代碼是不一樣的。
babel-register 則提供了動態編譯。換句話說,我們的源代碼能夠真正運行在生產環境下,不需要 babel 編譯這一環節。
我們先在項目下安裝 babel-register:
npm install --save-dev @babel/register
然後在入口文件中 require:
require("babel-register");
require("./index.js");
然後,就不需要手動對index.js轉碼了。
需要注意的是,babel-register只會對require命令加載的文件轉碼,而不會對當前文件轉碼。另外,由於它是實時轉碼,所以只適合在開發環境使用。
3.8@babel-node
babel-cli
工具自帶一個babel-node
命令,提供一個支持ES6的REPL環境。它支持Node的REPL環境的所有功能,而且可以直接運行ES6代碼。
它不用單獨安裝,而是隨babel-cli一起安裝。然後,執行babel-node就進入PEPL環境。
$ babel-node
> (x => x * 2)(1)
2
babel-node命令可以直接運行ES6腳本。將上面的代碼放入腳本文件es6.js,然後直接運行。
$ babel-node es6.js
2
babel-node也可以安裝在項目中。
$ npm install --save-dev babel-cli
然後,改寫package.json
{
"scripts": {
"script-name": "babel-node script.js"
}
}
請不要在生產環境中使用 babel-node,因爲它是動態編譯源代碼,應用啓動速度非常慢。
3.9syntax-dynamic-import
這個插件主要解決動態引入模塊的問題
function nDate() {
import('moment').then(function(moment) {
console.log(moment.default().format());
}).catch(function(err) {
console.log('Failed to load moment', err);
});
}
nDate();
不然就會報以下錯誤:
Module build failed: SyntaxError: 'import' and 'export' may only appear at the top level,
or (import 和 export只能在最外層,也就是不能用在函數或者塊中)
安裝:
npm install @babel-plugin-syntax-dynamic-import
使用:.babelrc
{
"plugins": ["syntax-dynamic-import"]
}