This article describes a recent multi-page project in which splitChunks of WebPack4 are used to split code packages

The project framework

The project has two entry files, home and Topic, which mainly include:

  • React, MOBx, antD as the basic framework of the project,
  • Echarts (drawing) and D3 (drawing) are the larger component libraries used for some of the pages in the project
  • SRC project common component code, etc
  • Other non-public code.

Both entry files are loaded asynchronously with react-loadable

import Loadable from 'react-loadable'; . const LoadableLogin = Loadable({ loader: () => import('.. /.. /common/components/login'), loading: Loading, }); .Copy the code

The webpack configuration is as follows:

module.exports = {
  ...
  mode: 'production', entry: {// multi-entry home:'./src/home',
    topic: './src/topic',
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'? Config. Build. AssetsPublicPath: config. Dev. AssetsPublicPath,}, plugins: [new HtmlWebpackPlugin ({/ filename/home page:'home.html',
      template: './template.html'}), new HtmlWebpackPlugin({// topic page filename:'topic.html',
      template: './template.html',
      inject: true,}),],... }Copy the code

splitChunks

chunks:

  • All: Separates files regardless of whether they are dynamically loaded or not. All packages are introduced when the page first loads
  • Async: Separates asynchronously loaded files. The files are not imported for the first time and are imported only to components that need to be imported asynchronously.
  • Initial: Separates asynchronous and non-asynchronous files. If a file is imported asynchronously and non-asynchronously, it is packaged twice (note the difference with all) to separate the packages that the page needs to load the first time.

MinSize: indicates the minimum package size of a file. The unit is byte. The default value is 30000

For example, a project has three entry files, a.js, b.js, and c.js, each with 100bytes. When we set minSize to 301, WebPack will pack them into one package, not split them into multiple packages.

AutomaticNameDelimiter: the connector

Suppose we generate a common file named vendor, A.js, and b.js that depend on it, and we set the connector to “~”, then the resulting file is Vendor ~a~ B.js

The maximum number of parallel requests at the maxInitialRequests entry point, which defaults to 3

If we set it to 1, each entry file will be packaged as only one file

MaxAsyncRequests Maximum number of asynchronous requests. Default: 5

If we set it to 1, each entry file will be packaged as only one file

Priority relationship

MaxInitialRequest/maxAsyncRequests <maxSize <minSize.

CacheGroups Customizes rules for splitting packages

Test can configure regex and write function as a packing rule. Other attributes can be inherited from splitChunks. Priority is important to set the priority of the package. (In combination with practice)

minChunks

Minimum number of introductions

practice

We start with a simple configuration that packages up the common code

   splitChunks: {
      chunks: 'all'// Initial, async, and all minSize: 30000, // Forming new blocks maxAsyncRequests: 5, // maxInitialRequests: 3, // Maximum number of initializing requests automaticNameDelimiter:'~'// Package separator name:true, cacheGroups: {vendors: {// Package two pages with the common code minChunks: 2, vendors: {vendors: {// Introduce two or more packaged names:'vendors'// Split packet name chunks:'all'}}},Copy the code

The common code for the two entry files is packaged under the Vendor folder, including some third-party packages such as Echarts D3 AmCharts and common code under SRC.

This is certainly not what we want! The following problems exist:

  • In fact, when we enter the website, the first step is to enter a landing page, which only needs the basic framework code of the project, such as react,react-dom.antd, etc. We can use all(or INITIAL) to package them separately as the package that must be loaded on the front page

  • The public package we packaged, when the page first loads, only wants to load in the synchronized Common package, so we need a synchronized Common package

  • Some asynchronously loaded packages like echarts, D3, and some SRC are packaged into a separate asynchronously loaded package using Async

We modify cacheGroups to:

CacheGroups: {vendors: {vendors: {// Project base frameworks, etc. chunks:'all'.test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
          priority: 100,
          name: 'vendors',},'async-commons'{// asynchronously load common packages, components, etc. Chunks:'async',
          minChunks: 2,
          name: 'async-commons', priority: 90,}, Commons: {// Other synchronous loading public packet chunks:'all',
          minChunks: 2,
          name: 'commons',
          priority: 80,
        },
      }
Copy the code

  • Async-common: async-common is a tripartite package in which two entry files are loaded asynchronously and lazy loading code is made by using react-loader, such as echarts,d3, etc

  • Vendors: Include React, React-DOM, ANTD, etc

  • Commons: Synchronized code that references more than two times

Here are two things to note:

  • Notice the setting of priority here, vendors> async-Commons > Commons. We package react,react-dom and so on first, and then the Common parts. If the vendors are set to a lower precedence than the two Common parts, So react, react-DOM will be packaged into the Common package, and will not be regenerated into vendors packages.

  • If we remove the Commons configuration here, it will generate a topic~home package. After we configure async-common to extract the common package loaded asynchronously, By default, synchronously loaded public packages will be packaged to generate a named ‘topic~home’ package with the automaticNameDelimiter connector ‘~’, which is exactly the same as the Commons package,

Ok! As we requested, this first page load would only introduce vendors Commons packages, not async-Common packages, which is cool! The pursuit of more refined we, think again, can do some better optimization?

So far our packaging of files looks like this:

Analyze:

  • Async-common contains self-written SRC components and third-party components

  • In async-common, echarts, Zrender (echarts introduced) and D3 are relatively large. In combination with the project, only part of the page we need echarts (d3 is the same), so we can consider extracting d3 and echarts, which are two relatively large packages. Waiting until a page needs to be loaded asynchronously greatly reduces the size of async-common.

Modify the

CacheGroups: {vendors: {vendors: {// Base framework chunks:'all'.test: /(react|react-dom|react-dom-router|babel-polyfill|mobx)/,
          priority: 100,
          name: 'vendors'}, d3Venodr: {// Separate the large d3 package and specify the page to load asynchronously when neededtest: /d3/, priority: 100, // Set async-commons higher than async-commons, avoid packing async-common name:'d3Venodr',
          chunks: 'async'}, echartsVenodr: {// Load the echarts package asynchronouslytest/ / (echarts | zrender), priority: 100, / / than the async - Commons priority name:'echartsVenodr',
          chunks: 'async'
        },
        'async-commons': {// other asynchronous load packet chunks:'async',
          minChunks: 2,
          name: 'async-commons', priority: 90,}, Commons: {// other synchronous load packets chunks:'all',
          minChunks: 2,
          name: 'commons',
          priority: 80,
        },
      }
Copy the code

[new HtmlWebpackPlugin({// home page filename:'home.html',
      template: './template.html',
      chunks: ['vendors'.'commons'.'home'],}), new HtmlWebpackPlugin({// topic page filename:'topic.html',
      template: './template.html',
      chunks: ['vendors'.'commons'.'topic'],}),],Copy the code

Later also do other split and optimization, probably the largest package to keep around 100K, of course, also do not recommend special small, because browser HTTP1 may support 6 downloads of files, too much may be counterproductive. We can do different split methods according to their own projects, in a word, is to make the project more perfect online operation, give users a better experience ~