preface
What is SplitChunks? Simply speaking, SplitChunks is a plug-in for extracting or separating code in Webpack. Its main function is to extract common code, prevent code from being repeatedly packaged, split large JS files, and merge scattered JS files.
When it comes to front-end optimization, extracting common code is essential. Before Webpack, extracting common code was done manually, and the SplitChunks plugin is configured to extract common code for you. The original intention of Webpack’s founders was to have more time to write more code, so the manual work was left to Webpack.
Therefore, the SplitChunks plug-in is a necessary skill for the front-end advancement. The usage of SplitChunks plug-in is described in detail below.
A worker must sharpen his tools before he can profit his work
This is because the SplitChunks plug-in extracts modules and packages them to generate JS files. First learn to package generated JS file name, or it is not good to confirm the package generated JS file is not what you want. There are several ways to name packaged JS files in Webpack.
1, the output filename
This option names the packaged entry JS file, which is the app.js file shown below.
-
In a single-entry Vue project, the entry file before packaging is configured in vue.config.js
module.exports = { configureWebpack:{ entry:'./src/main.js', } } Copy the code
The packaged entry file is configured in vue.config.js
module.exports = { configureWebpack:{ output:{ filename: 'js/appCS.js' }, } } Copy the code
The js file is generated in the directory specified by the output:path option, which defaults to the root directory /. The packaging result is shown below
-
In a multi-entry Vue project, the entry file before packaging is configured in vue.config.js
module.exports = { configureWebpack:{ entry: { appCS: './src/main.js' }, } } Copy the code
The packaged entry file is configured in vue.config.js
module.exports = { configureWebpack:{ output:{ filename: 'js/[name].js' }, } } Copy the code
[name] is the name of the entry file module. The packaging result is shown below
App.js is added because when configureWebpack values are objects, they are merged into the final configuration using webpack-merge. To remove app.js you can configure it with chainWebpack. Configuration is as follows
module.exports = { chainWebpack: config =>{ config.entryPoints.delete('app').end().entry('appCS').add('./src/main.js') } } Copy the code
The packaging result is shown below
2, the output chunkFilename
This option names packaged non-entry JS files, which are shown in the red box below
It is configured in vue.config.js
module.exports = {
configureWebpack:{
output:{
chunkFilename: 'CS.[name].js'
},
}
}
Copy the code
The packaging result is shown below
However, output.chunkfilename does not change the chunk-0a4e15c9 name field.
3, webpackChunkName
WebpackChunkName: The name of the block. [Request] can be interpreted as the actual parse file name. It can be used in route lazy loading
function load(component) {
return () => import(/* webpackChunkName: "[request]" */ `views/${component}`)
}
const routes = [
{
{
path: '/apiApply',
name: 'apiApply',
component: load('api_manage/api_apply'),
}
}
]
Copy the code
The packaging result is shown below
SRC /views/ API_manage/API_apply /index.vue The JS file generated by this component packaging is API_manage – API_apply.48227bf7.
You can also write this without [request].
component: () =>import(/* webpackChunkName: "api_manage-api_apply"*/ 'src/views/api_manage/api_apply'),
Copy the code
If you look at other JS files, there are chunk-xxx.js files, as shown in the red box below
The names of these JS files are set in the SplitChunks plugin.
Plug-in configuration options
chunks
Option to determine which modules to extract.- The default is
async
: only asynchronously loaded modules are extracted and packaged into a file.- Asynchronously loaded modules: pass
import('xxx')
orrequire(['xxx'],() =>{})
Loaded modules.
- Asynchronously loaded modules: pass
initial
If XXX is loaded asynchronously and asynchronously in the project, the XXX module will be extracted twice and packaged into different files.- Synchronously loaded modules: pass
import xxx
orrequire('xxx')
Loaded modules.
- Synchronously loaded modules: pass
all
: Modules loaded asynchronously or synchronously are extracted and packaged into a file.
- The default is
minSize
Option: Specifies the minimum size of the module to be extracted before compression. The unit is bytes. The default value is 30000.maxSize
Option: Package the extracted module to generate a file size that cannot exceed the maxSize value. If it does, split it and package it to generate a new file. The unit is byte. The default value is 0, indicating that the size is not limited.minChunks
Option: indicates the minimum number of references for the module to be extracted. If the number of references exceeds or equals to the minChunks value, the module can be extracted.maxAsyncRequests
Options: Maximum on demand (asynchronous) load count, default is 6.maxInitialRequests
Options: The number of js files (including entry files) that can be loaded at the same time when the packaged entry file is loaded. The default value is 4.- Let me start with priorities
maxInitialRequests
/maxAsyncRequests
<maxSize
<minSize
. automaticNameDelimiter
Option: package the separator of the generated JS file name. Default is~
.name
Option: Name of the package generated JS file.cacheGroups
Options, core focus,Configure the extraction module scheme. Each of these represents a scenario for extracting a module. The following iscacheGroups
The rest of the choices are consistent with the outside, ifcacheGroups
Each item has, according to the configuration, no use of external configuration.test
Option: Match the resource path or name of the module to extract. Values are regular or functions.priority
Option: Priority of the scheme. A larger value indicates that the scheme is preferred when extracting modules. The default value is 0.reuseExistingChunk
Options:true
/false
. fortrue
If the current module to be extracted is already in the package generatedjsFile, the module will be reused, rather than packaging the currently extracted module to generate a new onejsFile.enforce
Options:true
/false
. fortrue
, ignoreminSize
.minChunks
.maxAsyncRequests
andmaxInitialRequests
External configuration options.
SplitChunks are used in real projects to give you a better understanding of the configuration options.
Start by looking at the default configuration of SplitChunks in Vue Cli3. In the Vue Cli3 source code configuration
The default configuration is as follows:
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
}
}
}
}
}
}
};
Copy the code
Start by installing the Webpack-bundle-Analyzer plug-in to visually analyze packaged files.
npm install webpack-bundle-analyzer --save-dev
Copy the code
Introduce plug-ins in vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports={
configureWebpack:config =>{
return {
plugins:[
new BundleAnalyzerPlugin()
]
}
}
}
Copy the code
After the package is packaged, the browser automatically opens http://127.0.0.1:8888/, as shown below
It contains a chunk-vendors. Js file. Is the VENDORS schema JS file packaged in cacheGroups.
You can use the name option to change the names of chunk-vendors. Js files, with the following code
vendors: {
name: `app-chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
Copy the code
Once packaged, the chunk-Vendors. js file has become an app-chunk-vendors.js file with the same contents.
3. Split the import file
Take the scheme out of cacheGroups and wrap it up.
cacheGroups: {
vendors: false,
common: false
}
Copy the code
The two JS files app.a502ce9a.js and chunk-be34ce9a.ceff3b64.js are generated by packaging the entry file main.js in the project.
For example, the app.js file contains element-ui, moment, jquery, vue, router, Store, and jsencrypt. These are all introduced in 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;Copy the code
The index.html generated by the package reads as follows.
<body>
<div id=app></div>
<script src=/js/app.a502ce9a.js></script>
</body>
Copy the code
Indicates that app.js is loaded at the beginning of the project, which will affect the loading time of the first screen. You can remove some of the intrusions from main.js that you don’t need in the first screen for the time being, such as these.
import JsEncrypt from 'jsencrypt'; import treeSelect from 'fxft-tree-select'; Vue.use(treeSelect); Import base from 'service/base'; Vue.use(base); Import print from 'service/print'; Vue.use(print);Copy the code
When you look at the diagram after packaging, you will find that jsencrypt and other contents are missing from app.js.
Why chunk-be34ce9a.js is also packaged from main.js? Because there’s this code in main.js:
Import base from 'service/base'; Vue.use(base);Copy the code
Look at the service/base.js file
import('./Export2Excel').then(res => {
res.export_json_to_excel(defaultOpition);
})
Copy the code
Export2Excel. Js is loaded asynchronously in service/Export2Excel
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
Copy the code
Similarly, file-Saver and XLSX are also loaded asynchronously, so file-saver and XLSX will be extracted and packaged to generate chunk-be34ce9a.js files.
By default, modules loaded asynchronously or indirectly asynchronously in main.js are packaged separately to generate a JS file.
What if you want to pack all the modules loaded from node_modules into a SINGLE JS file? Vue Cli3 already did that for us.
cacheGroups: {
vendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
}
Copy the code
At its core is the test option, which matches the modules loaded by the project from node_modules and extracts the package to generate chunk-vendors. Js files. After packaging, I searched node_modules from the diagram and found that many files still contained modules loaded from node_modules, not as expected.
This is the chunks option, and the initial value indicates how many times XXX will be extracted and packaged into different files if XXX is loaded asynchronously or synchronously in the project. The core-JS library is loaded into every file in the project, so it extracts multiple times.
You can pack all modules loaded from node_modules into a JS file by changing the chunks option to all (both asynchronously and synchronously loaded modules are extracted and packaged into one file).
It turns out that chunk-vendors. Js is a bit too big, 1.91MB, and is the JS file that needs to be loaded during project initialization. If the size is too big, the first screen takes too long to load. There are two ways to optimize it
The first one uses externals to optimize. See my other article, Webpack, for a detailed explanation of the externals usage.
The second is optimized with SplitChunks. For example, to extract element from chunk-vendors. Js, configure it in cacheGroups:
element: {
chunks: 'all',
name: `element-ui`,
test: /[\\/]element-ui[\\/]/,
priority: 0,
},
Copy the code
Note the priority option, which, to extract element separately, must be larger than the value of priority in the vendors schema, otherwise it cannot be extracted.
Once packaged, you can see that Element is packaged to generate a new elite-ui.js file, and chunk-vendors. Js becomes 1.27MB, less than the original 1.91MB. In addition, you can extract XLSX, moment, jquery and other third-party dependencies by yourself.
4. Split and merge non-entry files
In the diagram, in addition to entry files, there are many JS files, most of which are generated by packaging components in the project.
/*webpackChunkName:”[request]”*/ webpackChunkName:”[request]”*/
In the figure, base_info_manage-group_info_set-ability_bind_set.85b419A1. js is views/base_info_manage/group_info_set/bility_bind_set /index.vue This component is generated as a package.
Base_info_manage -group_info_set-ability_bind_set-edit.08f91768.js is the view /base_info_manage/group_info_set/bility_bind_s in the project Et /edit.vue this component is generated as a package.
In addition, chunk-5C1416e3.1cbCB0ec. js is similar to base_info_manage-group_info_set-ability_bind_set-edit.08f91768.js. Only the SRC/API content is missing. Furthermore, modules such as API /common.js, API /ability_bind_set.js, edit.vue, mixins, etc. have been repeatedly packaged several times.
These modules can be extracted using SplitChunks. Avoid repeated packaging and reduce the overall size of files generated by packaging.
Configured in cacheGroups
api: {
name: 'api',
test: /[\\/]api[\\/]/,
priority: 0,
},
Copy the code
The name option is mandatory when extracting multiple modules to package a build file.
API /common.js and API /ability_bind_set.js have been extracted into APi.05AD5193.js
The Mixins module is then extracted and configured in cacheGroups
mixins: {
name: 'mixins',
test: /[\\/]mixins[\\/]/,
priority: 0,
},
Copy the code
After packaging, we look at the analysis diagram and find that the mixins module has been extracted into mixins.8d1d6f50.js.
The edit.vue module is then extracted and configured in cacheGroups
base_info_manage: {
name: 'base_info_manage',
test: /[\\/]base_info_manage[\\/]/,
minChunks: 2,
priority: 0,
},
Copy the code
The minChunks option must be 2 because edit.vue is referenced twice and index.vue is referenced only once. If it is 1 then index.vue is also extracted. If it is above 2, edit.vue will not be extracted.
After packaging, the analysis diagram shows that the edit.vue module has been extracted into base_info_manage.d5C14c01.js.
If you find the base_info_manage.d5C14c01.js file too large, there are two ways to handle it.
The first is usingmaxSize
The size of the generated file cannot exceed the maxSize value. If the size exceeds the maxSize value, the file must be extracted and packaged to generate a new file.
base_info_manage: { name: 'base_info_manage', test: /[\\/]base_info_manage[\\/]/, minChunks: 2, priority: 0, maxSize: 102400},Copy the code
When you look at the diagram after packaging, you will see that base_info_manage.js has been split into five small JS files.
The second method is to continue extraction by subfolders under the base_info_manage folder, for example, the base_info_manage folder has a subfile named group_info_set. Configured in cacheGroups
group_info_set: {
name: 'group_info_set',
test: /[\\/]base_info_manage[\\/]group_info_set[\\/]/,
minChunks: 2,
priority: 10,
},
Copy the code
The group_info_set module set in base_info_manage.js has been extracted into the group_info_set.js file. The base_info_manage.d5C14c01.js file has also been reduced accordingly.
In addition, contents in SRC/API, SRC /mixins and SRC /service can be combined and packaged to generate a JS file, replacing mixins.8d1d6f50.js and APi.05AD5193.js generated before
common: {
test: /[\\/]api[\\/]|[\\/]mixins[\\/]|[\\/]src[\\/]service[\\/]/,
priority: 0,
name: 'common',
},
Copy the code
Without further ado, you can use SplitChunks to control Webpack and generate js files in your own projects as described above, until each JS file in the dist/ JS folder is the js file you expect to generate.
Five, the summary
The essence of using SplitChunks to control the content of Webpack generated JS files is to prevent modules from being repackaged, split large JS files, and merge scattered JS files. The ultimate goal is to reduce the size and frequency of requests for resources. Since the two are contradictory, use SplitChunks according to the actual situation of the project, and remember the golden mean.