打包部署
Webpack 应用分割与生产环境部署
SplitChunks
代码分割是提升 Web 性能表现的重要分割,我们常做的代码分割也分为公共代码提取与按需加载等方式。公共代码提取即是将第三方渲染模块或者库与应用本身的逻辑代码分割,或者将应用中多个模块间的公共代码提取出来,划分到独立的 Chunk 中,以方便客户端进行缓存等操作。
不同于 Webpack 3 中需要依赖 CommonChunksPlugin 进行配置,Webpack 4 引入了 SplitChunksPlugin,并为我们提供了开箱即用的代码优化特性,Webpack 会根据以下情况自动进行代码分割操作:
- 新的块是在多个模块间共享,或者来自于 node_modules 目录;
- 新的块在压缩之前的大小应该超过 30KB;
- 页面所需并发加载的块数量应该小于或者等于 5;
- 初始页面加载的块数量应该小于或者等于 3;
SplitChunksPlugin 的默认配置如下:
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
值得一提的是,这里的 chunks 选项有 initial
, async
与 all
三个配置,上述配置即是分别针对初始 chunks、按需加载的 chunks 与全部的 chunks 进行优化;如果将 vendors 的 chunks 设置为 initial
,那么它将忽略通过动态导入的模块包包含的第三方库代码。而 priority 则用于指定某个自定义的 Cache Group 捕获代码的优先级,其默认值为 0。在 common-chunk-and-vendor-chunk 例子中,我们即针对入口进行优化,提取出入口公共的 vendor 模块与业务模块:
{
splitChunks: {
// 禁止默认 splitChunks 行为,防止生成 a~b.js 这样的公用包
default: false,
cacheGroups: {
commons: {
chunks: "initial",
minChunks: 2,
maxInitialRequests: 5, // The default limit is too small to showcase the effect
minSize: 0 // This is example is too small to create commons chunks
},
vendor: {
test: /node_modules/,
chunks: "initial",
name: "vendor",
priority: 10,
enforce: true
}
}
}
}
我们也可以设置 node_modules 中依赖按照 NPM 包方式分割:
{
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(
/[\\/]node_modules[\\/](.*?)([\\/]|$)/,
)[1];
// npm package names are URL-safe, but some servers don't like @ symbols
return `npm.${packageName.replace('@', '')}`;
},
}
runtimeChunk
Webpack 的 optimization 还包含了 runtimeChunk 属性,当该属性值被设置为 true 时,即会为每个 Entry 添加仅包含运行时信息的 Chunk;当该属性值被设置为 single 时,即为所有的 Entry 创建公用的包含运行时的 Chunk。我们也可以在代码中使用 import 语句,动态地进行块划分,实现代码的按需加载:
// Webpack 3 之后支持显式指定 Chunk 名
import(/* webpackChunkName: "optional-name" */ "./module")
.then((module) => {
/* ... */
})
.catch((error) => {
/* ... */
});
webpackJsonp([0], {
KMic: function(a, b, c) {
...
},
co9Y: function(a, b, c) {
...
},
});
如果是使用 React 进行项目开发,推荐使用 react-loadable 进行组件的按需加载,他能够优雅地处理组件加载、服务端渲染等场景。Webpack 还内建支持基于 ES6 Module 规范的 Tree Shaking 优化,即仅从导入文件中提取出所需要的代码。
构建目标
UMD
Webpack 将 output 中的 libraryTarget 设置为 umd 即可编译为 UMD 模式:
(function (define) {
define(function () {
// ...
});
})(
typeof module === "object" && module.exports && typeof define !== "function"
? function (factory) {
module.exports = factory();
}
: define
);
如果在代码里使用了 export default xxx
这种形式作为导出,那么最终输出的模块中会出现 libraryName.default
这种形式。如果希望以 default 作为默认入口,那么可以通过设置 libraryExport 为 default,来将 default 的值设置为 libraryName 的值本身。