在項目開發過程當中,咱們每每須要引入babel來解決代碼兼容性的問題。目前有三種方式,分別是
babel-polyfill,babel-runtime和babel-preset-env
,那麼這三種方式有什麼區別,結合webpack打包出來的效果哪一種比較優呢,下面咱們來對比一下。css
開始對比以前,咱們須要初始化一個webpack項目node
npm init babel-test
複製代碼
這裏咱們不打算安裝webpack 4.X的版本,安裝一下3.X的版本便可webpack
npm i -D webpack@3.7.0
複製代碼
在項目根目錄下新建一個webpack.config.js文件,配置以下git
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// JavaScript 執行入口文件
entry: {
app: ['./main.js']
},
output: {
// 把全部依賴的模塊合併輸出到一個 bundle.js 文件
filename: 'bundle.js',
// 輸出文件都放到 dist 目錄下
path: path.resolve(__dirname, './dist'),
},
devServer: {
contentBase: path.join(__dirname, "dist"),
inline: true
},
module: {
rules: [
{
// 用正則去匹配要用該 loader 轉換的 CSS 文件
test: /\.css$/,
// use: ['style-loader', 'css-loader?minimize'],
use: ExtractTextPlugin.extract({
// 轉換 .css 文件須要使用的 Loader
use: ['css-loader']
})
},
{
test: /\.js$/,
use: ['babel-loader']
}
]
},
plugins: [
new ExtractTextPlugin({
// 從 .js 文件中提取出來的 .css 文件的名稱
filename: `[name].css`,
})
]
};
複製代碼
package.json配置以下es6
{
"name": "babel-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --config webpack.config.js",
"dev": "webpack-dev-server --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.11",
"extract-text-webpack-plugin": "^3.0.2",
"style-loader": "^0.20.3",
"webpack": "^3.7.0",
"webpack-cli": "^1.5.3",
"webpack-dev-server": "^2.11.1"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0"
}
}
複製代碼
其中涉及到的包執行一下npm install
安裝一下便可,這裏就不贅述了。這裏的部分包接下來會重複提到,說明爲何要這樣裝github
項目初始化完成後的項目結構以下web
咱們往main.js裏面寫入一些代碼chrome
const elements = [1, 2, 3].map((item) => {
return (
console.log('9999')
)
});
console.log(elements);
async function azumia() {
console.log('begin');
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000)
})
console.log('done');
}
azumia();
console.log(Object.values({ 1: 2 }));
console.log(Array.isArray([]));
複製代碼
babel-polyfill 是爲了模擬一個完整的ES2015+環境,旨在用於應用程序而不是庫/工具。而且使用babel-node時,這個polyfill會自動加載。這裏要注意的是babel-polyfill是一次性引入你的項目中的,而且同項目代碼一塊兒編譯到生產環境。並且會污染全局變量。像Map,Array.prototype.find這些就存在於全局空間中。npm
因此這裏將其安裝到生產環境json
npm install babel-polyfill --save
複製代碼
webpack.config.js中這樣配置便可
entry: {
app: ['babel-polyfill','./main.js']
}
複製代碼
咱們執行一下一下命令,開始打包
npm run start
複製代碼
打包出來的bundle.js的文件大小爲259K
,而加入babel-polyfill以前的包的大小僅爲4K
,體積大了許多。 那麼咱們能不能作到按需引用babel-polyfill,從而減少包的大小呢?答案是能夠的,這就要靠babel-runtime來實現了。
babel-runtime不會污染全局空間和內置對象原型。事實上babel-runtime是一個模塊,你能夠把它做爲依賴來達成ES2015的支持。
好比環境不支持Promise,你能夠在項目中加入
require(‘babel-runtime/core-js/promise’)
複製代碼
來獲取Promise。
這樣咱們就彌補了babel-polyfill的缺點,達到了按需加載的效果。可是在實際項目開發過程當中,咱們每每會寫不少新的es6 api,每次都要手動引入相應的包比較麻煩,維護起來也不方便,每一個文件重複引入也形成代碼的臃腫。
要解決這個問題,就要用到 babel-plugin-transform-runtime
,它會分析咱們的 ast 中,是否有引用 babel-rumtime 中的墊片(經過映射關係),若是有,就會在當前模塊頂部插入咱們須要的墊片。
接下來咱們嘗試一下,先安裝babel-runtime和babel-plugin-transform-runtime
npm install --save babel-runtime
npm install --save-dev babel-plugin-transform-runtime
複製代碼
因爲 babel-runtime只是集中了polyfill的library,對應須要的 polyfill 都是要引入項目中,並跟項目代碼一塊兒打包的,因此就要加入到生產環境依賴中
下面在.babelrc中加入如下配置
{
"plugins": ["transform-runtime"]
}
複製代碼
執行打包命令,打包出來的bundle.js的大小爲63K
,比完整引入polyfill小了好多。可是事物都有兩面性,babel-runtime有個缺點,它不模擬實例方法
,即內置對象原型上的方法,因此相似Array.prototype.find,你經過babel-runtime是沒法使用的,這隻能經過 babel-polyfill 來轉碼,由於 babel-polyfill 是直接在原型鏈上增長方法。這就悲催了,難道仍是要完整引入babel-polyfill?其實還有一個解決的辦法,就是用babel-preset-env
babel-preset-env 能根據當前的運行環境,自動肯定你須要的 plugins 和 polyfills。經過各個 es標準 feature 在不一樣瀏覽器以及 node 版本的支持狀況,再去維護一個 feature 跟 plugins 之間的映射關係,最終肯定須要的 plugins。關於詳細的配置說明,請點擊這裏
咱們修改一下.babelrc的配置
{
"presets": [
["env", {
"targets": {
"chrome": 52,
"browsers": ["last 2 versions", "safari 7"]
},
"modules": false,
"useBuiltIns": "usage",
"debug": false
}]
]
}
複製代碼
其中的useBuiltIns就是是否開啓自動支持 polyfill,它能自動給每一個文件添加其須要的poly-fill。 咱們來嘗試一下,main.js中映入babel-polyfill
require('babel-polyfill')
複製代碼
你沒看錯,是要在main.js中引入,直接在webpack中配置貌似是不行的。
執行打包命令後,bundle.js的大小爲194K
,對比之下仍是要小了些的。
若是此時在瀏覽器中打開頁面,會發現以下報錯
這個問題的緣由及解決方案請點擊這裏,咱們修改一下babel-polyfill的引入方式,require 改成 import,將引入提高到前面
- require('babel-polyfill')
+ import 'babel-polyfill'
複製代碼
再次打包後便可
對比以上三種方案,咱們得出如下結論
方案 | 打包後大小 | 優勢 | 缺點 |
---|---|---|---|
babel-polyfill | 259K | 完整模擬ES2015+環境 | 體積過大;污染全局對象和內置的對象原型 |
babel-runtime | 63K | 按需引入,打包體積小 | 不模擬實例方法 |
babel-preset-env(開啓useBuiltIns) | 194K | 按需引入,可配置性高 | - |
方案沒有絕對的優劣,在開發過程當中仍是要根據實際狀況靈活運用。