这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
本文讲述webpack更高级的系列,如代码分割(Code Splitting)等。
代码分割(Code Splitting)
代码分离是webpack的优秀特性之一,此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
常用的代码分离方法有三种:
- 入口起点:使用
entry
配置手动地分离代码。 - 防止重复:使用 Entry dependencies 或者
SplitChunksPlugin
去重和分离 chunk。 - 动态导入:通过模块的内联函数调用来分离代码。
入口分离
看一个例子,我们在venors.js、index.js
中都引入了lodash
进行打包
// venors.js
import _ from 'lodash';
console.log(_.join(['Another', 'module', 'loaded!'], ' '));
// index.js
import _ from 'lodash';
import { add } from './math'
const result = add(1, 2);
console.log(result);
// webpack.config.js
mode: 'development',
entry: {
index: './src/index.js',
venors: './src/venors.js',
},
在多入口打包时,我们的入口文件中引入了重复的模块,那些重复模块都会被引入到各个 bundle 中,无疑增大了我们的代码文件大小,我们需要来进行剥离。
防止重复(删除重复模块代码)
入口依赖
通过配置 dependOn
option 选项,这样可以在多个 chunk 之间共享模块:
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
venors: {
import: './src/venors.js',
dependOn: 'shared',
},
shared: 'lodash'
},
再次打包后会生成三个文件index.bundle.js、venors.bundle.js、share.bundle.js
,我们公共模块lodash会被打包到share.bundle.js
中,就不会重复打包了,满足了我们的需求。此时如果我们要在一个 HTML 页面上使用多个入口时,还需设置 optimization.runtimeChunk: 'single'
,否则还会遇到这里所述的麻烦。
// webpack.config.js
optimization: {
runtimeChunk: 'single',
},
添加这项配置后,我们会多生成一个runtime.bundle.js
文件,将代码分得更细。
SplitChunksPlugin
除了上面说的入口依赖的方法,我们还可以利用SplitChunksPlugin
插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk来。
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
venors: './src/venors.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
使用 optimization.splitChunks
配置选项之后,现在应该可以看出,index.bundle.js
和 another.bundle.js
中已经移除了重复的依赖模块。需要注意的是,插件将 lodash
分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。执行 npm run build
查看效果。
动态导入
当涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 import()
语法 来实现动态导入。第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure
。让我们先尝试使用第一种。
将index.js代码改成如下所示,并将webpack.config.js改成单入口的index.js
// index.js
import { add } from './math'
import './venors'
const result = add(1, 2);
console.log(result);
(function () {
import('lodash')
.then(({ default: _ }) => {
console.log(_.join(['Another', 'module', 'loaded!'], ' '));
})
})()
运行npm run build
后我们的lodash也会被单独打包出来。
预获取/预加载模块(prefetch/preload module)
在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 "resource hint(资源提示)",来告知浏览器:
- prefetch(预获取):将来某些导航下可能需要的资源。浏览器在闲置时间预取资源,只要父 chunk 完成加载,webpack 就会添加 prefetch hint(预取提示)。
- preload(预加载):当前导航下可能需要资源
与 prefetch 指令相比,preload 指令有许多不同之处:
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
- 浏览器支持程度不同。