webpack 学习笔记:使用 babel(下)
本篇文章进一步探讨,如何在 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 协议》,转载必须注明作者和本文链接