webpack chunk

After a long time, I finally made up this article, which is a supplement to the WebPack workflow. This article mainly discusses from two dimensions, one is why to split chunk, the other is how to split chunk.

The implications of splitting chunks

① There is a strong connection between code splitting and the optimization of the first screen loading. With the idea of code splitting, we should separate out some code that will not be used in the first loading of the page and not request this part of the code when the page is loaded for the first time. Thus improving the speed of the first screen loading. (2) For example, many business codes have introduced the same third-party library, and the volume of this code is relatively large. When this part of the code is separated separately, the volume of chunk formed by the entrance can be reduced. Splitting files separately also improves cache hit ratio. What does that have to do with Chunk? The last file webpack outputs is the bundle, and chunk is the predecessor of the bundle. So splitting chunks is a big determinant of how bundles end up with code splitting, right

The chunk source

  1. Entry option in webPack configuration
  2. Import () loads functions on demand

    The import command, the require() function, does not generate chunk. For details, see how import(), import, require() works and their differences

  3. Optimization.splitchunk option in the WebPack configuration

entry

Correspondence 🖇ī¸

Each entry will form a chunk.

Entry can be a string, array, or object. The first two are single-entry; When configured in object format, there are multiple entries, such as Entry :{e1:’ entry 1′,e2:’ Entry 2′}. Detailed writing instructions.

For example 🌰

To facilitate the mapping between Chunk and entry, a multi-page application is used as an example.

The SRC ├ ─ ─... ├ ─ ─ pages | ├ ─ ─ home / / home page | | ├ ─ ─ the main, js | | └ ─ ─... | └ ─ ─ the about / / the about page | ├ ─ ─ the main, js | └ ─ ─... └ ─ ─...Copy the code

Configuration in webpack.config.js

// webpack.config.js
module.exports = {
  entry: {
    home: {"./src/pages/home/main.js"},
    about: "./src/pages/about/main.js",}};Copy the code

If vuecli3 scaffolding is used, the configuration in vue.config.js

// vue.config.js
module.exports = {
  pages: {
    home: {
      // The page entry (relative to the project root directory)
      entry: `src/pages/home/main.js`.// Other configuration items...
    },
    about: {
      // The page entry (relative to the project root directory)
      entry: `src/pages/about/main.js`.// Other configuration items...,}}};Copy the code

This will output 2 HTML and 2 main JS

Dist ├ ─ ─ home. HTML / / home is introduced. The [r]. Hash js ├ ─ ─ the about the HTML / / introduced about [hash] js └ ─ ─ assets ├ ─ ─ js | ├ ─ ─ home. [hash]. Js / / home entry form | ├ ─ ─ the about. [hash]. Js / / about the entry form | └ ─ ─... └ ─ ─...Copy the code

Summary 🎀

Each entry will form a chunk corresponding to the corresponding JS and HTML package. Entry corresponds to chunks, but it is not built to split chunks, it is built to build multi-page applications.

TIPS Entry. home and Entry. about can be configured as an object, and the supported options have nothing to do with splitting chunks, so I won’t cover them here.

import()

Correspondence 🖇ī¸

Import () A specific path splits all modules statically imported by the module into a separate chunk;

Import () can also be a fixed file in a fixed directory. For example, import(SRC /skin/${color}.less) packs each.less file in the SRC /skin directory into a new chunk. At run time, the variable color is computed and mapped to the corresponding file.

Import () must contain at least some information about the module’s path, and packaging can be restricted to a specific directory or set of files. That is, import([variable]/ XXX/XXX) is not allowed, but import([constant string path]/ XXX/XXX) is allowed.

For example 🌰

Suppose I now have a need for multiple skins, one skin at a time. The directory structure is as follows

SRC Exercises ── Main.js Exercises ─... ├ ─ ─ skin | ├ ─ ─ green. Less green theme style file | └ ─ ─ blue. The less blue theme style file └ ─ ─...Copy the code

This is a good case to use import() to form each topic into a chunk and load it on demand

function setSkin(skin) {
  import(`.. /.. /skin/${skin}/index.less`);
}
Copy the code

In addition, we can have more control over the chunk after the code is split by magic annotation, The configuration items include webpackInclude, webpackExclude, webpackChunkName, webpackMode, webpackPrefetch, and webpackPreload. Import () a specific path. WebpackInclude and webpackExclude are not available.

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

  • WebpackInclude and webpackExclude are used to filter files in a directory. This example wants all less files in the skin directory to be packaged. The default is no filtering, all participate in packaging, these two items can not be configured.

  • WebpackChunkName Specifies the name of the packaged file. In this example, webpackChunkName = “global-theme-“

  • WebpackMode is a key configuration associated with splitting chunks. You can choose from four values

    • ‘lazy’ each file generates a separate chunk of lazy load. Blue. The less – > blue. CSS, green. Less – > green. The CSS. Lazy loading means that specific files are loaded only when setSkin is executed.
    • ‘lazy-once’ only generates a single lazy chunk. That is, blue. Less and green.less will be packaged into a CSS file. Load the file only when setSkin is executed.
    • ‘eager’ does not generate independent chunks. If the setSkin function is defined in main.js, then blue.less and green.less are packaged in the main package.

      What is the difference between ‘eager’ and static import? There is no difference between static import and less files. If import() is a JS file, then the contents of the JS file will be executed only when setSkin is executed.

    • O (â•Ĩīšâ•Ĩ)o In the process of my own test, I found that green.less and blue.less were not packed into the main package and did not form chunk independently, and an error occurred when CALLING setSkin.

    Based on the above analysis, this example sets webpackMode = ‘lazy’ to meet the requirements of independent subcontracting. The default configuration is ‘lazy’, so you can omit ~

  • WebpackPrefetch and webpackPreload have little to do with chunk splitting. They mainly affect the loading time of files. The difference is explained briefly at the end of this section. The default is webpackPrefetch:true.

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Finally, you just need to configure a name

function setSkin(color) {
  /* webpackChunkName: "global-theme-" */
  import(`.. /.. /skin/${color}/index.less`);
}
Copy the code

After this, two theme files are packaged, named global-theme-0. CSS and global-theme-1.css.

Dist ├ ─ ─ assets/CSS | ├ ─ ─ global theme - 0. CSS | ├ ─ ─ global theme - 1. The CSS | └ ─ ─... └ ─ ─...Copy the code

The way it is referenced in the entry HTML

<link href=assets/css/global-theme-green.css rel=prefetch> <link
href=assets/css/global-theme-blue.css rel=prefetch>
Copy the code

Prefetch’s presentation: when the HTML is initialized, the theme style is not introduced. When the setSkin function is executed, the browser loads the corresponding CSS file content. Preload: when the HTML is initialized, the CSS file is loaded. When the setSkin function is executed, the corresponding CSS file takes effect. So a certain number of requests may be wasted with preload.

Summary 🎀

Import () splits chunks, but it is built for loading on demand, and it is not built specifically for splitting chunks.

optimization.splitchunk

Correspondence 🖇ī¸

Splitchunk has many configuration items. Each item in splitchunk.cacheGroups corresponds to a chunk.

Optimization.splitchunk is a built-in plug-in for webpack4+. (WebPack4 was previously CommonsChunkPlugin). Splitchunk can split chunks with finer granularity. It can split and extract chunks on demand formed by the synchronization chunk formed by entry and the chunk formed by import(). For example, in a multi-page application scenario where multiple entries reference a large third-party library such as Echarts, if the cacheGroup object is empty, each chunk formed by the entry will contain a chunk of Echarts code. Webpack By default, cacheGroup is already configured with two groups, but does not affect chunks formed by an entry. If you use scaffolding like vuecli3, which makes a few modifications to SplitChunk, its default (footnote) is to split echarts from the entry chunk and other node_modules into a separate chunk.

For example 🌰

For the following multi-page application, what if we wanted to split Echarts into a chunk and package it into a separate file?

The SRC ├ ─ ─... ├ ─ ─ pages | ├ ─ ─ home | | ├ ─ ─ the main, js (import)/App) vue) | | ├ ─ ─... | | └ ─ ─ App. Vue (import echarts) | └ ─ ─ the about | ├ ─ ─ the main, js (import)/App) vue) | ├ ─ ─... | └ ─ ─ App. Vue (import echarts) └ ─ ─...Copy the code

Based on the mapping between cacheGroup and Chunk

module.exports = {
  // ...
  optimization: {
    splitChunks: {
      / /... The default
      cacheGroups: {
        / /... The default
        / / add
         echarts: {
             // TODO configuration item}},}// ...
}

Copy the code

CacheGroup has a large number of configuration items (approximately 20). Here are some of the items I used in practice: Name, chunks, test, minChunks, minSize, and priority. (There are also some configuration items that I haven’t learned very well, so I won’t introduce them.)

  • Name is the packaged file name. When we set name to chunk-echarts, the packaged file name is chunk-echarts.[hash].js
module.exports = {
  // ...
  optimization: {
    splitChunks: {
      / /... The default
      cacheGroups: {
        / /... The default
        / / add
         echarts: {
             name: "chunk-echarts",,}}}// ...
}
Copy the code

Chunks and tests work together to determine which modules will be included in the cache group and form a chunk. It’s a “target lock” action.

  • Chunks can be configured as functions or as three specific values (‘initial’, ‘async’, ‘all’). Setting it to INITIAL means that the cache group only works on chunks formed by the entry, async only works on chunks formed by the on-demand import(), and all means that synchronous and on-demand chunks can be shared. In this example, echarts are static import imports that will exist in chunks formed by the entry, so chunks = initial.
  • The test value can be a function or a re. On top of the Chunks limits, module names are matched based on regs (or conditions defined in functions). We need to match echarts, so set test = /(echarts)/.

(The type option is also used to lock the target, which is not used in this example.)

module.exports = {
  // ...
  optimization: {
    splitChunks: {
      / /... The default
      cacheGroups: {
        / /... The default
        / / add
         echarts: {
             chunks: "initial".// Use all
             test: /(echarts)/,,}}}// ...
}
Copy the code

There are other limitations to splitting chunks. Only when these conditions are met can chunk be split successfully

  • MinChunks Minimum number of shared chunks before splitting. This is actually a limit on the frequency of use, which is explained in this example: How many chunks are echarts shared before it is split? Echarts are shared by two chunks before splitting. (Chunks are set to initial, and only two chunks, home and About, are considered.) Echarts is used in both, so minchunk can be set to 1 or 2. The default value is 1, which can meet requirements. You can omit the configuration.
  • MinSize Minimum number of bytes after splitting. This is a limit on the volume, echarts splits it out before compression > 8K, so minsize can be set to a number smaller than 8K. The default value is 20000, which can meet requirements. You can omit the configuration.
  • SplitChunks. MaxInitialRequests and entrance js parallel loading the file number (including the entry file) to limit. If the number of chunks to be split exceeds this number, the large chunk will be split separately first. In this case, the home page, along with the home.js entry, loads chunks of node_moudles and chunks of echarts. There are three of them, so maxInitialRequests should be set to a number >=3.
  • SplitChunks. MaxAsyncRequests to limit load parallel number on demand. Other points are similar to maxInitialRequests.

    Note that these are not configuration items for each cacheGroup, but for splitChunks.

  • Enforce is used to break the limit by telling Webpack to ignore minChunks, minChunks, maxAsyncRequests, and maxInitialRequests options and always create chunks for this cache group.
module.exports = {
  // ...
  optimization: {
    splitChunks: {
      / /... The default
      cacheGroups: {
        / /... The default
        maxInitialRequests:5./ / add
         echarts: {
             chunks: "initial".// Use all
             test: /(echarts)/.// minChunks:1,// default behavior
             // minsize:20000,// default behavior}},}// ...
}
Copy the code

Priority correlation

  • The priority configuration item is used to coordinate priorities between cache groups. By default, Webpack already has two sets of caches built into it. Modules from node_modules have a separate chunk, which has a priority of -10. (2) Modules that are repeatedly used (citation times >=2) will be independent of a chunk, and the priority of this group is -20. Echarts comes from node_modules, is reused, and satisfies our custom cache group. We want it to end up in our custom cache group, so priority is higher than -10, -20. The default value of priority is 0, which is greater than the priority of the default two groups, which can also be omitted.
module.exports = {
  // ...
  optimization: {
    splitChunks: {
      / /... The default
      maxInitialRequests:5.cacheGroups: {
        / /... The default
        / / add
         echarts: {
             chunks: "initial".// Use all
             test: /(echarts)/.// minChunks:1,// default behavior
             // minsize:20000,// default behavior
             // priority: 0,// Default behavior}},}// ...
}
Copy the code

The configuration items in the cacheGroup mentioned above, such as Chunks, maxAsyncRequests, maxInitialRequests, minChunks, minSize, can also be used as a global configuration item for splitChunks. When not configured in a cacheGroup, the value of splitchunks.xxx is used. If configured in cacheGroup, the value configured in cacheGroup is used. After user-defined chunk splitting, configure it in the entry so that it can be imported in HTML normally.

// vue.config.js
module.exports = {
  pages: {
    home: {
      // The page entry (relative to the project root directory)
      entry: `src/pages/home/main.js`.// Other configuration items...
      // loaded with the entry js
      chunks: ["chunk-echarts"."chunk-vendors"."chunk-common"."home"].// The default is ['chunk-vendors', 'chunk-common', 'index']
    },
    about: {
      // The page entry (relative to the project root directory)
      entry: `src/pages/about/main.js`.// Other configuration items...
      // loaded with the entry js
      chunks: ["chunk-echarts"."chunk-vendors"."chunk-common"."home"].// The default is ['chunk-vendors', 'chunk-common', 'index']}},chainWebpack: (webpackConfig) = > {
    // Other configuration...
    /* Active chunk */
    webpackConfig.optimization.splitChunks({
      cacheGroups: Object.assign( {}, defaultCacheGroups, config.customCacheGroups ), }); }};Copy the code

Chunk-echarts will be stored under Dist/Assets/JS after packaging. And it will be loaded at page startup along with the entry JS.

Summary 🎀

When the original chunk (entry chunks, import chunks) needs to be split again to extract common chunks or other uses, it is appropriate to send splitChunks.

added

  • The default configuration of SplitChunk in Vuecli3
{
  cacheGroups: {
    vendors: {
      name: 'chunk-vendors'.test: /[\\\/]node_modules[\\\/]/,
      priority: -10.chunks: 'async'
    },
    common: {
      name: 'chunk-common'.minChunks: 2.priority: -20.chunks: 'async'.reuseExistingChunk: true}}}Copy the code

The sample used

The code address

  1. Download to local
  2. npm i
  3. npm run build_test
  4. Looking at the dist folder, the package should look something like this:
Dist ├ ─ ─ home. HTML ├ ─ ─ the about the HTML └ ─ ─ assets ├ ─ ─ js | ├ ─ ─ home. [r]. Hash js / / home entry form | ├ ─ ─ the about. [r]. Hash js / / about the entry form | ├ ─ ─ the chunk - echarts. [r]. Hash of echarts js / / by splitchunk separation, separate out a file | ├ ─ ─ the chunk - vendors. [r]. Hash js / / splitchunk default configuration, Node_modules module of separate out a file | ├ ─ ─ app - theme - 0. [r]. Hash js / / cooperate with app - theme - 0. CSS | └ ─ ─ app - theme - 1. [r]. Hash js / / cooperate with app - theme - 1. The CSS └ ─ ─ CSS ├ ─ ─ home. CSS / / webpack default configuration will split the entrance js, CSS code in separate out a CSS file ├ ─ ─ the about the CSS / / webpack default configuration will split the entrance in js, CSS code and alone a CSS file ├ ─ ─ App - theme - 0. CSS / / by the import () dynamic introducing the theme files, produced a separate asynchronous load file └ ─ ─ app - theme - 1. The CSS / / by the import () dynamic introducing the theme files, produced a separate asynchronous load fileCopy the code
  1. Open home.html, about.html locally. It can start normally.

  2. On the home page, the browser console executes setSkin(‘green’). Be able to apply green themes

Reference documentation

webpack