SplitChunks插件是什么呢,简单的来讲就是Webpack中一个提取或分离代码的插件,主要做用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。html
提到前端优化,提取公共代码是必不可少的手段。在Webpack出现前,提取公共代码是人为去处理,而SplitChunks插件的做用就是经过配置让Webpack去帮你提取公共代码。Webpack创始人的初衷也是但愿能有更多时间写更多代码,因此这种纯体力的劳动交给Webpack去完成。前端
因此SplitChunks插件是前端进阶的一项必备技能,下面就详细介绍SplitChunks插件的用法。vue
由于SplitChunks插件会提取模块并打包生成js文件。先学会给打包生成的js文件命名,否则很差确认打包生成的js文件是否是你想要的。Webpack中给打包生成的js文件命名有如下几种方法。node
此选项给打包后的入口js文件命名,下图中的app.js文件就是打包后的入口js文件。jquery
module.exports = {
configureWebpack:{
entry:'./src/main.js',
}
}
复制代码
打包后的入口文件,在vue.config.js中配置module.exports = {
configureWebpack:{
output:{
filename: 'js/appCS.js'
},
}
}
复制代码
为何前面要加个js/
,由于该js文件是output:path
选项指定的目录下生成的,其默认为根目录/
。打包结果以下所示
module.exports = {
configureWebpack:{
entry: {
appCS: './src/main.js'
},
}
}
复制代码
打包后的入口文件,在vue.config.js中配置module.exports = {
configureWebpack:{
output:{
filename: 'js/[name].js'
},
}
}
复制代码
[name]
是入口文件模块名称。打包结果以下所示
module.exports = {
chainWebpack: config =>{
config.entryPoints.delete('app').end().entry('appCS').add('./src/main.js')
}
}
复制代码
打包结果以下所示
此选项给打包后的非入口js文件命名,那下图红框中所示就是非入口js文件 webpack
module.exports = {
configureWebpack:{
output:{
chunkFilename: 'CS.[name].js'
},
}
}
复制代码
打包结果以下所示 web
然而output.chunkFilename
并不能改变chunk-0a4e15c9这样名字字段。npm
webpackChunkName
:块的名称,[request]
可解释为实际的解析文件名。可在路由懒加载中使用element-ui
function load(component) {
return () => import(/* webpackChunkName: "[request]" */ `views/${component}`)
}
const routes = [
{
{
path: '/apiApply',
name: 'apiApply',
component: load('api_manage/api_apply'),
}
}
]
复制代码
打包结果以下所示 json
若是不用[request]
也能够这么写。
component: () =>import(/* webpackChunkName: "api_manage-api_apply"*/ 'src/views/api_manage/api_apply'),
复制代码
再看一下其它js文件,发现还有chunk-xxx.js类的文件,以下图红框中所示
chunks
选项,决定要提取那些模块。
async
:只提取异步加载的模块出来打包到一个文件中。
import('xxx')
或require(['xxx'],() =>{})
加载的模块。initial
:提取同步加载和异步加载模块,若是xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不一样的文件中。
import xxx
或require('xxx')
加载的模块。all
:无论异步加载仍是同步加载的模块都提取出来,打包到一个文件中。minSize
选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。maxSize
选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,若是超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。minChunks
选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。maxAsyncRequests
选项:最大的按需(异步)加载次数,默认为 6。maxInitialRequests
选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。maxInitialRequests
/ maxAsyncRequests
<maxSize
<minSize
。automaticNameDelimiter
选项:打包生成的js文件名的分割符,默认为~
。name
选项:打包生成js文件的名称。cacheGroups
选项,核心重点,配置提取模块的方案。里面每一项表明一个提取模块的方案。下面是cacheGroups
每项中特有的选项,其他选项和外面一致,若cacheGroups
每项中有,就按配置的,没有就使用外面配置的。
test
选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。priority
选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。reuseExistingChunk
选项:true
/false
。为true
时,若是当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。enforce
选项:true
/false
。为true
时,忽略minSize
,minChunks
,maxAsyncRequests
和maxInitialRequests
外面配置选项。配置选项不少,下面在实际项目中使用SplitChunks,让你更深入理解这些配置选项。
首先了解一下SplitChunks在Vue Cli3中的默认配置。在Vue Cli3源码中这样配置
module.exports = {
configureWebpack:config =>{
return {
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
cacheGroups: {
vendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: `chunk-common`,
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
}
}
}
};
复制代码
先安装个webpack-bundle-analyzer插件,能够可视化分析打包后的文件。
npm install webpack-bundle-analyzer --save-dev
复制代码
在vue.config.js中引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports={
configureWebpack:config =>{
return {
plugins:[
new BundleAnalyzerPlugin()
]
}
}
}
复制代码
打包后,会在浏览器自动打开http://127.0.0.1:8888/,内容以下所示
cacheGroups
中vendors这个方案打包出来的js文件。
能够用name
选项来修改chunk-vendors.js文件的名字,,代码以下
vendors: {
name: `app-chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
复制代码
打包后,会发现chunk-vendors.js文件已经变成了app-chunk-vendors.js文件,里面内容不变。
先去掉cacheGroups
里面的方案,再打包一下。
cacheGroups: {
vendors: false,
common: false
}
复制代码
例如app.js文件中有element-ui、moment、jquery、vue、router、store、jsencrypt等内容。这些都是在main.js中引入
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import JsEncrypt from 'jsencrypt';
import $ from 'jquery';
import ElementUI from 'element-ui';
Vue.use(ElementUI);
import treeSelect from 'fxft-tree-select';
Vue.use(treeSelect);
import moment from 'moment';
Vue.prototype.moment = moment;
//注册全局变量、全局函数
import base from 'service/base';
Vue.use(base);
//注册打印插件
import print from 'service/print';
Vue.use(print);
const vm = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
window.vm = vm;
复制代码
打包生成的index.html中有段代码是这么写。
<body>
<div id=app></div>
<script src=/js/app.a502ce9a.js></script>
</body>
复制代码
代表app.js是项目一开始就要加载的,会影响首屏的加载时间。那么能够在main.js中去掉一些在首屏中暂时用不到的引入,好比这些均可以暂时去掉。
import JsEncrypt from 'jsencrypt';
import treeSelect from 'fxft-tree-select';
Vue.use(treeSelect);
//注册打印插件
import print from 'service/print';
Vue.use(print);
复制代码
打包后再看分析图,会发现jsencrypt等内容都在app.js中都消失了。
为何说chunk-be34ce9a.js也是从main.js打包生成的?由于在main.js中有段代码:
//注册全局变量、全局函数
import base from 'service/base';
Vue.use(base);
复制代码
在看service/base.js文件中有
import('./Export2Excel').then(res => {
res.export_json_to_excel(defaultOpition);
})
复制代码
Export2Excel.js是异步加载,在service/Export2Excel.js中
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
复制代码
至关,file-saver、xlsx也是异步加载,因此file-saver、xlsx会被提取出来打包生成chunk-be34ce9a.js文件。
在默认配置下,main.js中异步加载或间接异步加载的模块,都会被另外打包生成一个js文件。
若是要把从node_modules中加载的模块所有打包到一个js文件中,要怎么作呢?Vue Cli3中的已经帮咱们作了。
cacheGroups: {
vendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
}
复制代码
其核心是test
选项,匹配项目从node_modules中加载的模块并提取打包生成chunk-vendors.js文件。 打包后再从分析图中搜索node_modules,发现仍是由不少文件中含有从node_modules中加载的模块,和预期的不同。
chunks
选项在做怪,值
initial
表示若是xxx在项目中异步加载或同步加载多少次,那么xxx这个模块也会被提取多少次,分别打包到不一样的文件中。core-js库在项目中每一个文件都会加载到,故它会提取屡次。
只要把chunks
选项的值改为all
(无论异步加载仍是同步加载的模块都是提取打包到一个文件中),就能够把从node_modules中加载的模块所有打包到一个js文件中。
第一种用externals来优化,看个人另外一篇文章Webpack之externals用法详解。
第二种用SplitChunks优化。例如要把element从chunk-vendors.js提取出来,要在cacheGroups
中配置:
element: {
chunks: 'all',
name: `element-ui`,
test: /[\\/]element-ui[\\/]/,
priority: 0,
},
复制代码
其中要注意priority
选项,要把element单独提取出来,priority
的值必须比vendors方案中的priority
的值大,否则提取不出来。
打包后可看到element被打包生成新的element-ui.js文件,chunk-vendors.js大小变成1.27MB,比原来的1.91MB有减少。此外能够本身提取xlsx、moment、jquery等第三方依赖。
在分析图中,除了入口文件,还有不少js文件,这些文件中有一大部分是项目中组件打包生成的。
若是在实现路由懒加载时,用到/*webpackChunkName:"[request]"*/
,那么在由组件打包生成的js文件名上,能够得知这个js文件是哪一个组件打包生成的。
base_info_manage-group_info_set-ability_bind_set-edit.08f91768.js是项目中views/base_info_manage/group_info_set/bility_bind_set/edit.vue这个组件打包生成的。
另外会发现chunk-5c1416e3.1cbcb0ec.js的内容怎么跟base_info_manage-group_info_set-ability_bind_set-edit.08f91768.js的内容类似,只少了src/api的内容。并且api/common.js、api/ability_bind_set.js、edit.vue、mixins等模块被重复打包了好几回。
能够用SplitChunks提取一下这些模块。避免重复打包,减小打包生成的文件整体大小。
在cacheGroups
中配置
api: {
name: 'api',
test: /[\\/]api[\\/]/,
priority: 0,
},
复制代码
当提取多个模块打包生成文件时,name
选项为必填。
cacheGroups
中配置
mixins: {
name: 'mixins',
test: /[\\/]mixins[\\/]/,
priority: 0,
},
复制代码
cacheGroups
中配置
base_info_manage: {
name: 'base_info_manage',
test: /[\\/]base_info_manage[\\/]/,
minChunks: 2,
priority: 0,
},
复制代码
minChunks
选项,必须为2,由于从上面分析图来看,edit.vue被引用了2次,而index.vue只被引用了1次。若是为1则index.vue也会被提取出来。若是为2以上的值则edit.vue不会被提取出来。
maxSize
选项,提取模块后打包生成的文件大小不能超过maxSize值,若是超过了,要再提取并打包生成新的文件。
base_info_manage: {
name: 'base_info_manage',
test: /[\\/]base_info_manage[\\/]/,
minChunks: 2,
priority: 0,
maxSize: 102400
},
复制代码
打包后再看分析图会发现base_info_manage.js已经被分割成五个小js文件了。
cacheGroups
中配置
group_info_set: {
name: 'group_info_set',
test: /[\\/]base_info_manage[\\/]group_info_set[\\/]/,
minChunks: 2,
priority: 10,
},
复制代码
打包后再看分析图会发现base_info_manage.js中的group_info_set模块集合已经被提取到group_info_set.js文件中。base_info_manage.d5c14c01.js文件也有相应的减少。
另外能够把src/api、src/mixins、src/service中的内容合并打包生成一个js文件,替代以前打包生成的mixins.8d1d6f50.js和api.05ad5193.js
common: {
test: /[\\/]api[\\/]|[\\/]mixins[\\/]|[\\/]src[\\/]service[\\/]/,
priority: 0,
name: 'common',
},
复制代码
其余的就很少说,能够按照上面所讲在本身的项目用SplitChunks插件来控制Webpack打包生成本身想要的js文件,直到dist/js文件夹中每一个js都是你所预想的生成的js文件为止。
用SplitChunks插件来控制Webpack打包生成的js文件的内容的精髓就在于,防止模块被重复打包,拆分过大的js文件,合并零散的js文件。最终的目的就是减小请求资源的大小和请求次数。因这二者是互相矛盾的,故要以项目实际的状况去使用SplitChunks插件,需切记中庸之道。