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

  • chunksOption to determine which modules to extract.
    • The default isasync: only asynchronously loaded modules are extracted and packaged into a file.
      • Asynchronously loaded modules: passimport('xxx')orrequire(['xxx'],() =>{})Loaded modules.
    • initialIf XXX is loaded asynchronously and asynchronously in the project, the XXX module will be extracted twice and packaged into different files.
      • Synchronously loaded modules: passimport xxxorrequire('xxx')Loaded modules.
    • all: Modules loaded asynchronously or synchronously are extracted and packaged into a file.
  • minSizeOption: Specifies the minimum size of the module to be extracted before compression. The unit is bytes. The default value is 30000.
  • maxSizeOption: 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.
  • minChunksOption: 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.
  • maxAsyncRequestsOptions: Maximum on demand (asynchronous) load count, default is 6.
  • maxInitialRequestsOptions: 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 prioritiesmaxInitialRequests / maxAsyncRequests <maxSize <minSize.
  • automaticNameDelimiterOption: package the separator of the generated JS file name. Default is~.
  • nameOption: Name of the package generated JS file.
  • cacheGroupsOptions, core focus,Configure the extraction module scheme. Each of these represents a scenario for extracting a module. The following iscacheGroupsThe rest of the choices are consistent with the outside, ifcacheGroupsEach item has, according to the configuration, no use of external configuration.
    • testOption: Match the resource path or name of the module to extract. Values are regular or functions.
    • priorityOption: Priority of the scheme. A larger value indicates that the scheme is preferred when extracting modules. The default value is 0.
    • reuseExistingChunkOptions:true/false. fortrueIf 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.
    • enforceOptions:true/false. fortrue, ignoreminSize.minChunks.maxAsyncRequestsandmaxInitialRequestsExternal 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 usingmaxSizeThe 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.