本篇文章進一步探討,如何在 webpack 中使用 babael 對原始碼做處理。
回顧
上一節中,我們使用瞭如下的配置來處理原始碼:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ......
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties']
}
}
}
]
},
mode: 'none'
};
babel 提供的 @babel/preset-env
的預置包,只能處理包含在 ECMAScript 2020 標準中(截止到2020.09)的所有新語法特性,如果原始碼中包含 class (public) field declarations 特性(Stage 3),就要額外引入 @babel/plugin-proposal-class-properties 來實現該特性的正確轉碼。
預設 @babel/preset-env
會將 ES2015-ES2020 原始碼全部轉換為 ES5 程式碼。
那麼是否掌握這些就足夠了呢?不是。還有兩個最佳化點,值得我們學習。
- 一個是與 browserslist 的整合
- 還有一個,就是要解決 polyfills 的引入問題
與 browserslist 的整合
@babel/preset-env 提供了一個 targets 選項,支援以 browserslist query 的形式,指定專案要相容的瀏覽器版本(或者 Node 版本)。這樣就可以依據要相容的瀏覽器範圍,有選擇性的轉碼,達到最小化轉換程式碼,得到更小的打包體積。
比如,像下面這樣設定:
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead',
}
]
],
不過,與 browserslist 行為不同的是,如果未指定 targets,@babel/preset-env 並不會使用預設的 defaults
關鍵字,而只是粗暴地將所有 preset-latest 所包含的語法特性全部轉換為相容 ES5 的,沒有考慮瀏覽器範圍。
當然,不指定這樣 targets 是非常不推薦的。推薦在專案中顯式指定 targets 的值,即使是 defaults:
{
// 等同於 browserslist 的 `defaults`,等價於設定了
// targets: '> 0.5%, last 2 versions, Firefox ESR, not dead',
targets: 'defaults',
}
defaults 還可與其他 query 一起使用:
{
targets: 'defaults, not ie 11, not ie_mob 11',
}
解決 polyfills 的引入
@babel/preset-env
解決了語法轉碼的問題,但是 API 的支援還需要引入額外的 polyfill。舉一個例子,如果專案程式碼需要相容 IE9+ 瀏覽器,但程式碼中使用了 Primise API,這個 API 是 IE9 環境中不支援的,語法轉碼時,也不會做處理,如果不引入 Promise polyfill,那麼執行到此的時候,程式碼必然是會報錯的。
@babel/preset-env
中兩個選項,專門負責 polyfill 引入。
- useBuiltIns:配置 @babel/preset-env 如何處理 polyfill 引入,最常用的值是 ‘usage’,即按需引入
- corejs:引入的 polyfill 的程式碼來源
舉一個例子:
presets: [
[
'@babel/preset-env',
{
targets: 'defaults, not ie 11, not ie_mob 11',
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true }
}
]
],
useBuiltIns: 'usage'
表示根據原始碼,按需引入 corejs 提供的 polyfill 檔案;corejs: { version: 3, proposals: true }
則表示使用的是 corejs 的 v3 版本,而且支援對 ECMAScript proposals 中涉及到的特性的 polyfill 的引入。
實戰案例
接下來,我透過幾個例子來說明 targets
、useBuiltIns
和 corejs
這三個選項的使用。
先來看看專案結構:
webpack-demo/
│ webpack.config.js
│
└─src
index.js
template.html
安裝依賴:
$ npm i -D webpack webpack-cli html-webpack-plugin
$ npm i -D babel-loader babel/core @babel/preset-env @babel/plugin-proposal-class-properties core-js
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
// See: https://webpack.js.org/loaders/babel-loader/
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
// 稍後填入
}
]
],
plugins: [
'@babel/plugin-proposal-class-properties'
]
}
}
}
]
},
plugins: [
// See: https://webpack.js.org/plugins/html-webpack-plugin/
new HtmlWebpackPlugin({ template: './src/template.html' })
],
mode: 'none'
};
index.js:
class Bar {
set publicVar(x) { console.log(x) }
}
class Foo extends Bar {
publicVar = 0
#privateVar = 0
getPrivateVar() {
return this.#privateVar
}
}
const foo = new Foo()
foo.publicVar
foo.getPrivateVar()
template.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body></body>
</html>
一、先配置如下:
{
// See: https://babeljs.io/docs/en/babel-preset-env#no-targets
targets: 'defaults',
}
$ npx webpack --watch
......
Built at: 2020-09-28 11:26:25 ├F10: PM┤
Asset Size Chunks Chunk Names
bundle.js 7.97 KiB 0 [emitted] main
index.html 244 bytes [emitted]
列印出來的 bundle.js t大小為 7.97 KiB。
二、接下來按需引入 polyfills:
{
targets: 'defaults',
// https://babeljs.io/docs/en/babel-preset-env#usebuiltins
useBuiltIns: 'usage',
// https://babeljs.io/docs/en/babel-preset-env#corejs
corejs: { version: 3, proposals: true }
}
......
Built at: 2020-09-28 11:28:13 ├F10: PM┤
Asset Size Chunks Chunk Names
bundle.js 106 KiB 0 [emitted] main
index.html 244 bytes [emitted]
因為 corejs 會引入一些內部的核心的工具函式,打包體積一下子變大到 106 KiB。
三、去掉對 IE 的支援
{
targets: 'defaults, not ie 11, not ie_mob 11',
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true }
}
Built at: 2020-09-28 11:30:55 ├F10: PM┤
Asset Size Chunks Chunk Names
bundle.js 47.9 KiB 0 [emitted] main
index.html 244 bytes [emitted]
看到,包體積一下子回落到 47.9 KiB 的大小。
四、構建生產環境包
基於【三】中的配置,我們再調整下 webpack mode 為 ‘production’,來看看最終構建出來的生產包的尺寸:
module.exports = {
// ......
mode: 'production'
}
Built at: 2020-09-28 11:34:50 ├F10: PM┤
Asset Size Chunks Chunk Names
bundle.js 14.3 KiB 0 [emitted] main
index.html 209 bytes [emitted]
尺寸減少為 14.3 KiB,算是可以接受的範圍了。
五、取消 corejs 的 proposals
如果我們沒有使用新的 ECMAScript proposals API 的需求,可以再進一步修改配置,不啟用 corejs 的 proposals
選項(置為 false
)。
{
targets: 'defaults, not ie 11, not ie_mob 11',
useBuiltIns: 'usage',
- corejs: { version: 3, proposals: true }
+ corejs: { version: 3, proposals: false }
}
Built at: 2020-09-28 11:42:26 ├F10: PM┤
Asset Size Chunks Chunk Names
bundle.js 13.8 KiB 0 [emitted] main
index.html 209 bytes [emitted]
最終尺寸可達到 13.8 KiB!
(完)
本作品採用《CC 協議》,轉載必須註明作者和本文連結