When it comes to front-end packaging tools, the first place to go is undoubtedly Webpack. But the front end is evolving fast, with new things popping up every now and then, and parcels and rollups have come before that on the packaging side. The collision of various tools draws advantages from each other and promotes the development of technology.

Webpack4 has support for zero configuration features and block packaging has been optimized. The CommonsChunkPlugin has been removed and now uses optimization.splitchunks instead.

So let’s start with the splitChunks.

The default

First, WebPack automatically splits code blocks based on the following criteria:

  • New code blocks can be shared references, or modules can come from the node_modules folder
  • New code block larger than 30KB (size before min+gziped)
  • For code blocks loaded on demand, the maximum number of parallel requests should be less than or equal to 6
  • The maximum number of concurrent requests should be less than or equal to 4 for the initially loaded code block

Block packaging only affects on-demand modules by default, because optimizing packaging for the initial block also affects the number of script tags in the HTML, increasing the number of requests.

Let’s look at some examples to understand packaging by default.

Modules are all imported synchronously

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'

console.log(_.join(['a'.'b'].'~'))

ReactDOM.render(
  <div>SplitChunk</div>.document.getElementById('root'))Copy the code

The default only affects the on-demand module, so everything is packaged together.

There are dynamic module imports

The ECMAScript proposal compliant import() syntax is used first

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import(/* webpackChunkName: "async-jquery" */ 'jquery').then(component= > {
  console.log(component)
})

console.log(_.join(['a'.'b'].'~'))

ReactDOM.render(
  <div>SplitChunk</div>.document.getElementById('root'))Copy the code

Here jquery uses dynamic import, and you can see that jquery is packaged separately

React loads on demand

Similarly, we tried loading react on demand using the react-router solution

The AsyncModule module asynchronously loads the Dashboard as described above

import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter, Route} from 'react-router-dom'
import AsyncModule from './AsyncModule.jsx'
import _ from 'lodash'
import $ from 'jquery'

console.log(_.join(['a'.'b'].'~'))

ReactDOM.render(
  <div>
    <BrowserRouter>
      <Route path='/' component={AsyncModule} />
    </BrowserRouter>
  </div>,
  document.getElementById('root')
)
Copy the code

From the packaging result, you can see that the modules loaded on demand are packaged into 0.js.

With webPack optimizations for packaged chunks by default, look at the splitChunks configuration item.

Configuration items

Related configuration items:

module.exports = {
  / /...
  optimization: {
    splitChunks: {
      chunks: 'async'.minSize: 30000.minChunks: 1.maxAsyncRequests: 5.maxInitialRequests: 3.automaticNameDelimiter: '~'.name: true.cacheGroups: {}}}}Copy the code
  • Chunks: indicates which code needs to be optimized. There are three optional values: Initial (initial chunks), async(chunks loaded on demand), and all(chunks loaded on demand). The default is Async
  • MinSize: indicates the minimum module size before compression. The default value is 30000
  • MinChunks: indicates the number of times to be referenced. The default value is 1
  • MaxAsyncRequests: Maximum number of parallel requests that can be loaded on demand (default: 5)
  • MaxInitialRequests: The maximum number of parallel requests for an entry. Default is 3
  • AutomaticNameDelimiter: Named connector
  • Name: indicates the name of the split block. By default, the block name and hash value are automatically generated
  • CacheGroups: cacheGroups. The attributes of the cache group include test, Priority, and reuseExistingChunk
    • Test: Used to control which modules are matched by this cache group
    • Priority: priority of cache group packaging
    • ReuseExistingChunk: No new block is created if the current block contains a module that already exists

These are the configuration items, but let’s focus on chunks and cacheGroups.

chunks

“Chunks” is “initial”, “async”, “all”. The default is Async, whose behavior was described in the first part of this article, so let’s look at the behavior of the other two.

The initial, all mode allocates all modules from node_modules to a cache group called vendors; All code that is referenced at least twice is assigned to default’s cache group.

// indexA.js
import './Dashboard.jsx'

// indexB.js
import './Dashboard.jsx'

// Dashboard.jsx
import React from 'react'
Copy the code
// webpack.config.js
splitChunks: {
  chunks: 'initial'
}
Copy the code

The packaging behaves as described above, generating two code blocks, vendors Default.

Can be configured optimization. SplitChunks. CacheGroups. Default: false to disable the default cache group.

// webpack.config.js
splitChunks: {
  chunks: 'initial'.cacheGroups: {
    default: false}}Copy the code

As for the difference between all and Initial, check out this article Webpack 4 — Mysterious SplitChunks Plugin

Initial mode optimizes and packages asynchronous and non-asynchronous modules separately. All optimizes both asynchronous and non-asynchronous packaging. That is, moduleA is introduced asynchronously in indexA and synchronously in indexB, and the lower moduleA will appear in two packaged blocks for INITIAL and only one for ALL.

cacheGroups

Using cacheGroups, you can customize the packaging of blocks.

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'

// indexB.js
import React from 'react'
import ReactDOM from 'react-dom'
import('lodash')
import $ from 'jquery'

// webpack.config.js
optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons'.chunks: 'initial'.minChunks: 2}}}}Copy the code

According to the Webapck segmentation conditions introduced at the beginning, some public modules are packaged into Commons, and the custom packaged blocks have a priority of 0, so now the public modules are packaged into Commons, rather than the default packaged blocks mentioned above, which are vendors with negative precedence.

But why is lodash not packed together? So let’s review the difference between initial and all. Let’s experiment with the effect of all.

// indexA, indexB
// webpack.config.js
optimization: {
    splitChunks: {
        cacheGroups: {
            commons: {
                name: 'commons'.chunks: 'all'.minChunks: 2}}}}Copy the code

As expected, Lodash was packed together.

Extracting third-party libraries

Finally, take a look at the separation of parts of third-party libraries previously used by CommonsChunkPlugin. You can think about how to do that.

Had mentioned in the above set chunks: initial | | all third-party libraries can be extracted. However, it extracts all third libraries, so we need to customize a cacheGroup when we only extract react and react-DOM.

// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'

// webpack.config.js
entry: {
    indexA: path.join(__dirname, 'src/indexA.js')},optimization: {
    splitChunks: {
        chunks: 'all'.cacheGroups: {
            vendors: {
                test: /react/.name: 'vendors'}}}}Copy the code

We rewrote the Vendors package block to only pack modules that match React, so the function of the CommonsChunkPlugin was achieved.

or

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

// webpack.config.js
entry: {
    indexA: path.join(__dirname, 'src/indexA.js'),
    vendor: ["react"."react-dom"]},optimization: {
    splitChunks: {
        cacheGroups: {
            vendor: {
                name: "vendor".chunks: "initial"}}}}Copy the code

Add a vendor portal to the WebPack portal that contains all libraries that need to be bundled separately, and then set this bundle chunk in the cacheGroups: Initial | | all, also can get indexA repeat the extraction of library to the vendor and the vendor packaging block.

optimization.runtimeChunk

Finally mention runtimeChunk, through optimization. RuntimeChunk: true options, webpack will add a runtime (runtime) contains only additional blocks of code to each entry. (This will cause each entry to load one more copy of the runtime code)

conclusion

Webpack4’s splitChunks feature is quite powerful, but it is recommended to use the default mode or extract third-party libraries.

Reference material

  • Without the CommonsChunkPlugin, how can we subcontract?
  • Webpack 4 — Mysterious SplitChunks Plugin
  • Learn about webPack4’s splitChunk plug-in step by step