日常用框架开发过程中,Webpack已经内置并设置了一些代码分片的默认项,所以代码分片这块的一些细节比较容易被忽略;代码分片是Webpack作为打包工具所特有的一项技术,通过这项技术,可以把代码按照特定的形式进行拆分,使得用户不必一次加载全部代码,而是按需加载。
下面我们看一下几种实现代码分片的方法:
1.通过入口划分代码
//webpack.config.js
{
entry: {
index: './index.js',
lib: ['lib-a', 'lib-b', 'lib-c']
}
}
// index.html
<script src="dist/lib.js"></script>
<script src="dist/index.js"></script>
这种拆分方法主要适合那些将接口绑定在全局对象上的库,因为业务代码中的模块无法直接引用库中的模块,二者属于不同的依赖树。
2.通过 CommonsChunkPlugin 提取代码
CommonsChunkPlugin 是 webpack4 之前内部自带的插件(webpack4 之后替换为SplitChunks),它可以将多个chunk中公共的部分提取出来。
下面是 CommonsChunkPlugin 的配置提取代码流程:
配置
//webpack.config.js
const webpack = require('webpack');
module.exports = {
entry: {
app: './app.js',
about: './about.js'
},
output: {
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'commons', // 用于指定公共chunk的名字
filename: 'commons.js' // 提取后的资源文件名
})
]
}
//app.js
import React from 'react';
document.write('app.js', React.version);
//about.js
import React from 'react';
document.write('about.js', React.version);
执行打包后, 输出文件夹出了app.js,about.js 还多输出了 commons.js, 其中app.js,about.js 体积下降了不少, 这就是把react及其依赖模块都提到commons.js的结果。
提取vendor
虽然CommonsChunkPlugin 主要用于提取多入口之间的公共模块,但这不代表对于单入口的应用就无法使用。 我们仍然可以用它来提取第三方类库及业务中不常更新的模块,只需要单独为它们创建一个入口即可。
//webpack.config.js
module.exports = {
entry: {
app: './app.js',
vendor: ['react']
},
output: {
filename: '[name].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js'
})
]
}
//app.js
import React from 'react';
document.write('app.js', React.version);
为了将react从app.js提取出来,我们在配置中加入了一个入口vendor,并使其只包含react,这样就把react 变为app 和 vendor这两个chunk所共有的模块。
CommonsChunkPlugin 的不足
- 一个CommonsChunkPlugin只能提取一个vendor, 如果想提取多个vendor则需要配置多个插件,这会增加很多重复的配置代码;
- manifest实际上会使浏览器多加载一个资源,这对于页面渲染速度不太友好;
- 由于内部设计的一些缺陷,CommonsChunkPlugin 在提取公共模块的时候会破坏掉原🈶️Chunk中模块的依赖关系,导致难以进行更多优化。
3.通过 SplitChunks 实现代码分片
在使用CommonsChunkPlugin的时候,我们大多通过配置项将特定入口中的特定模块提取出来,也就是更贴近命令式的方式;而在使用SplitChunks时,我们只需要设置一些提取条件,如提取的模式、提取模块的体积等,某些模块达到这些条件后就会自动被提取出来,所以, SplitChunks 的使用更像是声明式的。
以下是SplitChunks的默认配置:
- 提取后的chunk可被共享或者来自node_modules目录
- 提取后的JavaScript chunk 体积大于20KB(压缩和gzip之前), CSS体积大于50KB
- 在按需加载过程中,并行请求的资源最大值小于等于30
- 在首次加载时,并行请求的资源最大值小于等于30
SplitChunks默认配置
下面是Webpack 内置 SplitChunks的默认项
//webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', //all 全部 chunk
//initial 入口 chunk, 对于异步导入的文件不处理
//async 异步chunk, 只对异步导入文件处理
minSize: 20000, //起步多少字节才开始做代码分割
minRemainingSize: 0,//通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块
minChunks: 1, // 当模块至少被引用多少次才会做代码分割
maxAsyncRequests: 30, //最大同时加载的模块数
maxInitialRequests: 30, //入口文件,同时加载的模块数
enforceSizeThreshold: 50000,// 强制执行拆分的体积阈值
cacheGroups: { //cacheGroups 可以理解成分离chunks时的规则
defaultVendors: {// defaultVendors 用于提取符合规则的所有node_modules
test: /[\\/]node_modules[\\/]/, // 对node_modules的依赖起作用
priority: -10, // 权限更高,优先分离,重要!!
reuseExistingChunk: true,//如果这个模块已经被打包过了,可忽略,不在打包
filename" 'vendors.js' //新增项, 分割代码命名
},
default: { //default 用于被多次引用的模块
minChunks: 2,// 公共模块最少复用过几次
priority: -20,// -10优先级别大于-20
reuseExistingChunk: true, //如果这个模块已经被打包过了,可忽略,不在打包
filename: 'common.js' //新增项, 分割代码命名
},
},
},
},
};
optimization.splitChunks更多配置项可以查看webpack官网: webpack.js.org/plugins/spl…
虽然大多数项目开发的时候,不需要单独去配置代码分片SplitChunks等配置项,但是小墨觉得随着企业项目的庞大,可能会遇到一些打包效率和性能优化和引用一些特殊三方依赖等问题;所以代码分片的实现方法还是需要去熟悉,并且把一些基本的配置过一遍,需要单独配置的时候能快速的去官网查询解决问题!