在 webpack 中編寫 JavaScript 代碼,可使用最新的 ES 語法,而最終打包的時候,webpack 會藉助 Babel 將 ES6+語法轉換成在目標瀏覽器可執行 ES5 語法。因此 Babel 是一個重要的知識點須要掌握。node
Babel 是 JavaScript 的編譯器,經過 Babel 能夠將咱們寫的最新 ES 語法的代碼輕鬆轉換成任意版本的 JavaScript 語法。隨着瀏覽器逐步支持 ES 標準,咱們不須要改變代碼,只須要修改 Babel 配置便可以適配新的瀏覽器。
舉例說明,下面是 ES6 箭頭函數語法的代碼:android
[1,2,3].map(n => n**2);
通過 Babel 處理後,能夠轉換爲普通的 ES5 語法:webpack
[1,2,3].map(function(n) { return Math.pow(n, 2); });
下面來介紹下 Babel 的安裝和功能及其配置文件。ios
Babel 自己本身帶有 CLI(Command-Line Interface,命令行界面)工具,能夠單獨安裝使用。下面咱們在項目中安裝 @babel/cli 和 @babel/core。web
npm i -D @babel/core @babel/cli
而後建立一個 babel.js 文件:chrome
// babel.js [1,2,3].map(n => n**2);
而後執行npx babel babel.js,則會看到輸出的內容,此時可能會看到輸出的內容跟源文件內容沒有區別,這是由於沒有加轉換規則,下面安裝@babel/preset-env。而後執行 CLI 的時候添加 --presets flag:npm
// 安裝 preset-env npm i -D @babel/preset-env // 執行 CLI 添加 --presets npx babel babel.js --presets=@babel/preset-env
最終輸出的代碼就是轉換爲 ES5 的代碼了:json
‘use strict’ [1,2,3].map(function(n) { return Math.pow(n, 2); });
若是要輸出結果到固定文件,可使用 --out-file 或 -o 參數:
npx babel babel.js -o output.js。
Tips: Babel 7 使用了 @babel 命名空間來區分官方包,所以之前的官方包 babel-xxx 改爲了 @babel/xxx。promise
除了使用命令行配置 flag 以外,Babel 還支持配置文件,配置文件支持兩種:
使用 package.json 的 babel 屬性;
在項目根目錄單首創建 .babelrc或者 .babelrc.js文件。
示例以下:瀏覽器
// package.json { ‘name’: ‘my-package’, ‘version’: ‘1.0.0’, ‘babel’: { ‘presets’: [‘@babel/preset-env’] } } // .babelrc { ‘presets’: [‘@babel/preset-env’] }
Babel會在正在被轉義的文件當前目錄中查找一個 .babelrc 文件。 若是不存在,它會向外層目錄遍歷目錄樹,直到找到一個 .babelrc 文件,或一個 package.json 文件中有 "babel": {}。
若是咱們但願在不一樣的環境中使用不一樣的 Babel 配置,那麼能夠在配置文件中添加 env 選項:
{ ‘env’: { ‘production’: { ‘presets’: [‘@babel/preset-env’] } } }
env 選項的值將從 process.env.BABEL_ENV 獲取,若是沒有的話,則獲取 process.env.NODE_ENV 的值,它也沒法獲取時會設置爲 "development"。
Babel 的語法轉換是經過強大的插件系統來支持的。Babel 的插件分爲兩類:轉換插件和語法解析插件。
不一樣的語法對應着不一樣的轉換插件,好比咱們要將箭頭函數轉換爲 ES5 函數寫法,那麼能夠單獨安裝 @babel/plugin-transform-arrow-functions 插件,轉換插件主要職責是進行語法轉換的,而解析插件則是擴展語法的,好比咱們要解析 jsx 這類 React 設計的特殊語法,則須要對應的 jsx 插件。
若是不想一個個的添加插件,那麼可使用插件組合 preset(插件預設,插件組合更加好理解一些),最多見的 preset 是 @babel/preset-env。以前的 preset 是按照 TC39 提案階段來分的,好比看到 babel-preset-stage-1 表明,這個插件組合裏面是支持 TC39< Stage-1 階段的轉換插件集合。
@babel/preset-env 是 Babel 官方推出的插件預設,它能夠根據開發者的配置按需加載對應的插件,經過 @babel/preset-env 咱們能夠根據代碼執行平臺環境和具體瀏覽器的版原本產出對應的 JavaScript 代碼,例如能夠設置代碼執行在 Node.js 8.9 或者 iOS 12 版本。
Babel 只負責進行語法轉換,即將 ES6 語法轉換成 ES5 語法,可是若是在 ES5 中,有些對象、方法實際在瀏覽器中多是不支持的,例如:Promise、 Array.prototype.includes,這時候就須要用 @babel/polyfill 來作模擬處理。@babel/polyfill 使用方法是先安裝依賴,而後在對應的文件內顯性的引入:
// 安裝,注意由於咱們代碼中引入了 polyfill,因此再也不是開發依賴(--save-dev,-D) npm i @babel/polyfill
在文件內直接 import 或者 require 進來:
// polyfill import ‘@babel/polyfill’ console.log([1,2,3].includes(1));
@babel/polyfill 雖然能夠解決模擬瀏覽器不存在對象方法的事情,可是有如下兩個問題:
直接修改內置的原型,形成全局污染;
沒法按需引入,Webpack 打包時,會把全部的 Polyfill 都加載進來,致使產出文件過大。
爲了解決這個問題,Babel 社區又提出了 @babel/runtime 的方案,@babel/runtime 再也不修改原型,而是採用替換的方式,好比咱們用 Promise,使用 @babel/polyfill 會產生一個 window.Promise 對象,而 @babel/runtime 則會生成一個 _Promise (示例而已)來替換掉咱們代碼中用到的 Promise。另外 @babel/runtime 還支持按需引入。下面以轉換 Object.assign 爲例,來看下 @babel/runtime 怎麼使用。
npm i -D @babel/plugin-transform-object-assign
編寫一個 runtime.js 文件,內容以下:
Object.assign({}, {a:1});
執行 npx babel runtime.js --plugins @babel/plugin-transform-runtime,@babel/plugin-transform-object-assign,最終的輸出結果是:
import _extends from ‘@babel/runtime/helpers/extends’; _extends( {}, { a:1 } );
代碼中自動引入了 @babel/runtime/helpers/extends 這個模塊(因此要添加 @babel/runtime 依賴啊)。
@babel/runtime也不是完美的解決方案,因爲 @babel/runtime 不修改原型,因此相似[].includes() 這類使用直接使用原型方法的語法是不能被轉換的。
Tips:’@babel/polyfill’實際是 core-js和
regenerator-runtime的合集,因此若是要按需引入’@babel/polyfill’的某個模塊,能夠直接引入對應的
core-js 模塊,可是手動引入的方式仍是太費勁。
鋪墊了這麼多,咱們繼續來說 @babel/preset-env,前面介紹了@babel/preset-env 能夠零配置的轉化 ES6 代碼,咱們若是要精細化的使用 @babel/preset-env ,就須要配置對應的選項了,在 @babel/preset-env 的選項中,useBuiltIns 和 target 是最重要的兩個, useBuiltIns 用來設置瀏覽器 polyfill,target 是爲了目標瀏覽器或者對應的環境(browser/node)。
preset-env 的 useBuiltIns
前面介紹了 @babel/polyfill 和 @babel/runtime 兩種方式來實現瀏覽器 polyfill,兩種方式都比較繁瑣,並且不夠智能,咱們可使用 @babel/preset-env 的 useBuildIn 選項作 polyfill,這種方式簡單並且智能。
useBuiltIns 默認爲 false,可使用的值有 usage 和 entry:
{ ‘presets’: [ ‘@babel/preset-env’, { ‘useBuiltnls’: ‘usage|entry|false’ } ] }
usage 表示明確使用到的 Polyfill 引用。在一些 ES2015+ 語法不支持的環境下,每一個須要用到 Polyfill 的引用時,會自動加上,例如:
const p = new Promise(); [1,2].includes(1); ‘foobar’.includes(‘foo’);
使用 useBuiltIns='usage' 編譯以後,上面代碼變成,真正的作到了按需加載,並且相似 [].includes() 這類直接使用原型方法的語法是能被轉換的:
‘use strict’ require(‘core-js/modules/es.array.includes’); require(‘core-js/modules/es.object.to-string’); require(‘core-js/modules/es.promise’); require(‘core-js/modules/es.string.includes’); var p = new Promise(); [1,2].includes(1); ‘foobar’.includes(‘foo’);
entry 表示替換 import "@babel/polyfill";(新版本的 Babel,會提示直接引入 core-js或者regenerator-runtime/runtime來代替 @babel/polyfill)的全局聲明,而後根據 targets 中瀏覽器版本的支持,將 polyfill 拆分引入,僅引入有瀏覽器不支持的 polyfill,因此 entry 相對 usage 使用起來相對麻煩一些,首先須要手動顯性的引入 @babel/polyfill ,並且根據配置 targets 來肯定輸出,這樣會致使代碼實際用不到的 polyfill 也會被打包到輸出文件,致使文件比較大。
通常狀況下,我的建議直接使用 usage 就知足平常開發了。
須要提一下的是,polyfill 用到的 core-js 是能夠指定版本的,好比使用 core-js@3,則首先安裝依賴 npm i -S core-js@3,而後在 Babel 配置文件 .babelrc 中寫上版本。
// .babelrc { ‘presets’: [ [ ‘@babel/preset-env’, { ‘useBuiltlns’: ‘useage’, ‘corejs’: 3 } ] ] }
假設但願代碼中使用 ES6 的模板字面量`語法,可是實際執行代碼的宿主瀏覽器是 IE 10 卻不支持,那麼咱們可使用</code>target
指定目標瀏覽器了。
{ ‘presets’: [ [ ‘@babel/preset-env’, { ‘targets’: { ‘browsers’: ‘IE 10’ } } ] ] }
若是咱們代碼是在 Node.js 環境執行的,則能夠指定 Node.js 的版本號:
{ ‘presets’: [ [ ‘env’, { ‘@babel/preset-env’: { ‘node: ‘8.9.3’ } } ] ] }
targets.browsers 須要使用 browserslist 的配置方法,可是其設置會被 targets.[chrome, opera, edge, firefox, safari, ie, ios, android, node, electron] 覆蓋;
targets.node 設置爲 true 或 "current" 能夠根據當前 Node.js 版本進行動態轉換。也能夠設置爲具體的數字表示須要支持的最低 Node.js 版本;
targets.esmodules 設置使用 ES Modules 語法,最新瀏覽器支持,這個在後面 Webpack 插件章節會詳細介紹如何實現 Modern Mode。
經過上面的內容,咱們已經掌握了 Babel 的基本用法,下面在 webpack 中使用 Babel 就變得很簡單了,首先安裝 npm 依賴,而後修改 webpack.config.js。
安裝依賴包:
// 安裝開發依賴 npm i webpack babel-loader webpack-cli @babel/core @babel/preset-env @babel/plugin-transform-runtime -D // 將 runtime 做爲依賴 npm i @babel/runtime -S
第二步建立 webpack.config.js 文件,內容以下:
// webpack.config.js module.exports = { entry: ‘./babel.js’, mode: ‘development’, devtool: false, module: { rules: [ { test: /\.js$/, use: [ { loader: ‘babel-loader’, options: { presets: [ [ ‘@babel/preset-env’, { useBuiltlns: ‘usage’ } ] ] } } ] } ] } }
上面的 webpack.config.js 文件直接將 Babel 的配置寫到了 options 中,還能夠在項目根目錄下建立 .babelrc 或者使用 package.json 的 babel 字段。小結 在本篇中,咱們學習了 Webpack 怎麼配置 Babel,但願對你們有所幫助,若是喜歡蘿蔔的文章,請你們持續關注,下一篇我將給你們介紹 webpack-dev-server 這個超好用的工具。