關於Babel配置項的這點事
- 2019 年 12 月 30 日
- 筆記
摘要: IE雖然沒落了,Babel還是要用的…
- 原文:關於Babel配置項的這點事
- 作者:tonyc726
Fundebug經授權轉載,版權歸原作者所有。
Babel作為一個JavaScript的語法編譯器,可以將ES6/7/8
程式碼轉為ES5
程式碼,從而在現有環境執行。
但是初次配置.babelrc
的時候,各種presets
、plugins
看的眼花繚亂,不知道如何下手,下面就自己學習Babel時遇到的問題做一下總結:
如果你是初次接觸babel,推薦閱讀阮一峰的《Babel 入門教程》
Plugin、Preset、Stage-X的關係
按照Babel官網的介紹,其實Preset和Stage-X都是歸屬到Plugin裡面的,只不過所覆蓋的範圍不同而已。
舉個例子,如果需要轉換ES2015(ES6)的語法,那麼你可以在.babelrc
的plugins
中按需引入check-es2015-constants
、es2015-arrow-functions
、es2015-block-scoped-functions
等等幾十個不同作用的plugin:
// .babelrc { "plugins": [ "check-es2015-constants", "es2015-arrow-functions", "es2015-block-scoped-functions", // ... ] }
但是Babel團隊為了方便,將同屬ES2015的幾十個Transform Plugins集合到babel-preset-es2015
一個Preset中,這樣你只需要在.babelrc
的presets
加入es2015
一個配置就可以完成全部ES2015語法的支援了:
// .babelrc { "presets": [ "es2015" ] }
另外,不論是Plugin還是Preset,有不少都有單獨屬於自己的配置項,具體如何操作的可以看一下官網的說明。
上面介紹了Plugin與Preset,那麼Stage-X就很好理解了,stage-0
、stage-1
、stage-2
、stage-3
、
分別對應的就是進入標準之前的5個階段,不同stage-4stage-x
之間存在依賴關係,數字越小,階段越靠後,靠後階段包含前面階段所有的功能,簡單理解就是stage-0
包含stage-1/2/3
的內容,所以如果你不知道需要哪個stage-x
的話,直接引入stage-0
就好了。
PS:
babel-preset-stage-4
已經整合入Presets不單獨發布了。
以上就是一些基礎概念,目前,官方推薦使用babel-preset-env
,它可以根據你的配置結合compat-table
來幫你自動引入你需要的plugins,它有很多配置項,下面介紹幾個常用的:
- targets:
{ [string]: number | string }
,默認{}
;
需要支援的環境,可選例如:chrome
, edge
, firefox
, safari
, ie
, ios
, node
,甚至可以指定版本,如node: "6.10"
或者node: "current"
代表使用當前的版本;
- targets.node:
number | string | "current" | true
;
指定node
的版本,例如:6.10
;
- targets.browsers:
Array<string> | string
;
指定需要兼容的瀏覽器清單,具體參考browserslist,例如:["last 2 versions", "safari >= 7"]
;
例如需要配置兼容["last 2 versions", "safari >= 7"]
的babel-preset-env
:
// .babelrc { "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }] ] }
此外,不同的plugins和presets或許有些功能是重複的,有些存在依賴關係,在配置的時候還有前後順序的不同,那麼Babel在運行的時候是怎麼處理的呢?總結一下,規律大概有以下幾點:
- plugins優先於presets進行編譯;
- plugins按照數組的index增序(從數組第一個到最後一個)進行編譯;
- presets按照數組的index倒序(從數組最後一個到第一個)進行編譯,因為作者認為大部分會把presets寫成
["es2015", "stage-0"]
,具體細節可以看這個。
babel-polyfill與babel-runtime的選擇
Babel默認只轉換新的JavaScript語法,而不轉換新的API,比如Iterator
、Generator
、Set
、Maps
、Promise
等等全局對象,以及一些定義在全局對象上的方法(比如Object.assign
)都不會轉碼,具體的可以參考babel-plugin-transform-runtime
模組的definitions.js文件。
babel-polyfill
與babel-runtime
就是為了解決這種全局對象或者全局對象方法不足的問題,而誕生的2種解決方式。
當然,你還可以用
promise-polyfill
此類Polyfill解決全局對象的問題; 或者用lodash
此類Utils解決Object.assign
這種方法擴展的問題。
先說說babel-polyfill
,它的做法比較暴力,就是將全局對象通通污染一遍,這樣做的壞處有幾點:
- 可能會增加很多根本沒有用到的polyfill;
- 可能會污染子模組的局部作用域,嚴重的或許會導致衝突;
但是,這樣做也有好處,如果你的運行環境比較low,比如說Android一些老機子,而你有需要大量使用Promise
、Object.assign
、Array.find
之類的全局對象或者其所屬方法,那麼使用babel-polyfill
,絕對是一勞永逸。
接著,再來說說babel-runtime
,相對而言,它的處理方式比較溫柔,套用步步高的廣告詞就是哪裡需要加哪裡,比如說你需要Promise
,你只需要import Promise from 'babel-runtime/core-js/promise'
即可,這樣不僅避免污染全局對象,而且可以減少不必要的程式碼。
不過,如果N個文件都需要Promise
,難道得一個個文件的加import Promise from 'babel-runtime/core-js/promise'
么,顯然不是,Babel已經為這樣情況考慮過了,只需要使用babel-plugin-transform-runtime
就可以輕鬆的幫你省去手動import
的痛苦,而且,它還做了公用方法的抽離,哪怕你有100個模組使用了Promise
,但是promise的polyfill僅僅存在1份,所有要的地方都是引用一地方,具體的配置參考如下:
// .babelrc { "presets": [ "env", "stage-0" ], "plugins": [ "transform-runtime" ], "comments": false }
寫在最後,我在Github上開了一個項目,做了幾個測試,有興趣的可以一起來試試看。
babel與webpack
關於babel
與webpack
結合使用的教程網上已經有很多了,有不少卻還在用v1.*
的版本(不推薦),從而在過渡到v2.*
或者v3.*
(推薦)的版本時,碰到一個關於babel
的配置問題,示例如下:
// .babelrc - webpack v1.* { "presets": [ "env", "stage-0" ] } // .babelrc - webpack v2.* - v3.* { "presets": [ ["env", { "modules": false }], "stage-0" ] }
很明顯,一眼就能看出相對於v1.*
的版本,v2.*
或者v3.*
版本多了"modules": false
這項配置,如果仔細看官網的遷移指南,你就能明白了,以前你可能需要用babel
來將ES6
的模組語法轉換為AMD
、CommonJS
、UMD
之類的模組化標準語法,但是現在webpack已經把這個事情做了,所以就不需要babel
來做了,但是babel
配置項中的modules
默認值是commonjs
,所以你需要將modules
設置為false
才行,不然就衝突了。
參考資料
- Babel 入門教程
- 如何寫好.babelrc?Babel的presets和plugins配置解析
- babel的polyfill和runtime的區別
- Babel 全家桶
- transform-runtime 會自動應用 polyfill,即便沒有使用 babel-polyfill
- 如何區分Babel中的stage-0,stage-1,stage-2以及stage-3(一)
本文先發佈於我的個人部落格《Babel筆記》,後續如有更新,可以查看原文。
版權聲明
轉載時請註明作者 Fundebug以及本文地址: https://blog.fundebug.com/2018/11/01/about-babel-configuration/
您的用戶遇到BUG了嗎?
.copyright *{box-sizing:border-box}