代码分块的作用
1、将构建出来的一个代码块,分解成多个小块,方便加载。
2、将业务代码和第三方库的代码分离,这样增加新的业务的时候就不会影响到第三方库代码的内容,也不会影响到浏览器的缓存了。
代码分块的方式
1、分离业务代码和第三方库(vendor)
2、按需加载(使用import()的语法)
配置
在开发过程中可能经常会遇到业务变更的情况这个时候就需要将业务代码和第三方库的代码进行分离,保证我们部署的时候第三方的代码缓存不被破坏,再就是uglifyjs-webpack-plugin开启缓存,缓存的命中方式是根据chunk的内容计算出来hash的值,所以如果不进行分块的话每次修改业务代码进行构建的时候都会出现缓存失效全部重新压缩编译的过程,耗费构建时间。再然后就是一份代码被多处引用又该如何将其分流出来呢?以下通过响应的配置可以更好地解决这些问题。
splitChunks: {
chunks: "all", //指定打包同步加载还是异步加载
minSize: 30000, //构建出来的chunk大于30000才会被分割
minRemainingSize: 0,
maxSize: 0, //会尝试根据这个大小进行代码分割
minChunks: 1, //制定用了几次才进行代码分割
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: "~", //文件生成的连接符
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/, //符合组的要求就给构建venders
priority: -10, //优先级用来判断打包到哪个里面去
filename: "vendors", //指定chunks名称
},
default: {
minChunks: 2, //被引用两次就提取出来
priority: -20,
reuseExistingChunk: true, //检查之前是否被引用过有的话就不被打包了
},
},
}
以上是一份splitChunks的配置,我们通过代码一步步了解每个配置的作用。前提是我们webpack入口都是单入口配置。
entry: "./src/index.js"
chunks属性
chunks的配置分为:all、async、initial三种,看一下区别在哪里?
all
chunks:'all' 作用是符合下面参数的条件就给强制分块,比如minSize:30000,就是大于30kb的chunks就给分块。
以下是代码块(chunk)大于30kb:

输出的结果:

文件内容:


可以看到大于30kb,业务代码和第三方库分块了。
为什么第三方库会被单独分割出来呢?看下方代码注释
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/, //有分块的情况下没其他配置的情况下webpack读取默认配置默认组匹配了node_modules的文件分割成一个单独的块。
priority: -10, //优先级用来判断打包到哪个里面去
filename: "vendors", //指定chunks名称
},
default: {
minChunks: 2, //被引用两次就提取出来
priority: -20,
reuseExistingChunk: true, //检查之前是否被引用过有的话就不被打包了
},
},
小于30kb的情况下代码并不会被分块。只会生成一个chunk。
那么对于异步加载(import())chunks:'all'的表现是什么样子呢?
做一个简单的demo试一下
import Vue from "vue";
import React from "react";
import ReactDom from "react-dom";
import lodash from "lodash";
import {getVersion} from './app'
// import {getName} from './name'
//异步加载
import('./name').then(({})=>{
getName();
})
function app(){
return <div>哈哈哈哈</div>
}
ReactDom.render(<App/>,document.getElementById('name'));
new Vue();
console.log('哈哈哈')
console.log($('#name').html())
getVersion();


可以看到异步加载的代码也被分割成了一个单独的块。这说明chunks:all 会分割异步加载和满足配置条件的代码。
async
chunks:'async':只会分割import()这种方式加载的模块成为一个单独的chunks,如果有多个import()呢
再来个demo:
import Vue from "vue";
import React from "react";
import ReactDom from "react-dom";
import lodash from "lodash";
// import {getVersion} from './app'
// import {getName} from './name'
//异步加载
import('./name').then(({getName})=>{
getName();
})
import('./app').then(({getVersion})=>{
getVersion();
})
function App(){
return <div>哈哈哈哈</div>
}
ReactDom.render(<App/>,document.getElementById('name'));
new Vue();
console.log('哈哈哈')
这个经过webpack构建之后的结果是:

最终多生成了俩chunk,并且webpack会在用到的时候通过webpackjsonp方法动态创建script标签加载相应的文件,我们在react和Vue的懒加载路由中使用的也是这种方式。
再深入一步,懒加载的文件中还有懒加载会是什么样子呢?

经过测试又多生成了一个chunk,加载顺序呢,就是promise代码正常的执行顺序,如果一个页面包含了异步加载模块但是这个模块加载失败,那接下来的异步加载是不会加载的。
initial
chunks:'initial' 这个的作用是:splitChunks就不会将同步加载和异步加载一起处理,而是分开处理。
举个例子: 我们有四个文件:
index.js
import React from "react";
import ReactDom from "react-dom";
import {getVersion} from './app'
//异步加载
import('./name').then(({getName})=>{
getName();
})
function App(){
return <div>哈哈哈哈</div>
}
ReactDom.render(<App/>,document.getElementById('name'));
getVersion();
console.log('哈哈哈')
app.js
import React from 'react';
function getVersion(){
// return loadsh.clone('0.0.1');
return '0.0.1';
}
export {
getVersion
}
name.js
import Vue from "vue";
function getName(){
return '哈哈哈'
}
new Vue()
export {
getName
}
com.js
import Vue from "vue";
function getName(){
return '哈哈哈'
}
new Vue()
export {
getName
}
以上三个文件的关系是:index.js分别同步引入app.js 异步引入name.js和com.js name.js和com.js又同步引入Vue。
经过构建结果是:name.js和com.js包含了Vue的代码并没有被分离到vendor中去,各自包含了一套vue代码。


为什么会这样呢,这就是将同步加载和异步加载分开处理的结果,index.js只解析到了异步引入name.js和com.js就不会再去解析接下来的异步加载,而name.js和com.js又会被当做另外两个独立入口进行解析内部的内容。于是vue的代码就被各自保留到了异步加载的chunks中去。 这个跟async就是刚好相反了,async就是只处理异步加载成独立的chunk,同步的都混合在了main.js中。
所以all配置的作用就是把这两者二合一。
在all模式下vue被提出来一个独立的chunks,并不混合在name.js和com.js中

这个原理就是:我只要一个异步加载了vue那就代表另外一个异步模块可用了,所以把vue给提取出来成为单独chunk
不过这里需要注意的是,如果在入口文件(index.js)Vue被引用过了,这两种模式Vue还是会被提取出来的。原因同上:入口文件已经加载了vue那就代表Vue在之后的使用一定存在。
总结:
async:适合只将异步加载模块分离成单独chunk而其他模块合并成为一个chunk。
all:适合在正常项目中提取同步异步公用模块到一个chunk中减少代码加载和代码量。
initial:这个配置的特性是:如果自己当前模块的引用模块引用过自己正在使用的库那么这个库会被提取到公用的chunks中去,如果没有被引用过,就会构建到自己生成的chunk当中去,如果自己的子模块引用过这个库,也会被提取成相应的以自身为起点的公用chunk,所以这个代码分割可以尽可能的减小初始化的时候的代码量,属于有需要再提取,而不是all下的发现了就提取。在使用上我觉得这种方式比较好。