Progressive configuration of webpack4 single and multiple pages

preface

Use the version of the package

Webpack ->4.3.0 babel-loader -> 8.0.5nPM ->6.4.1 webpack-cli ->3.3.1Copy the code

Each section corresponds to a demo

Modular Unpacking 1

Reference code Demo8

What is modular unpacking? For example, we need to introduce echarts, XLSX, LoDash and other large packages in the project. If you don’t unpack the code, it will be packaged into a JS file. If a new JS package is generated every time a package is released, the user will request echarts, XLSX, LoDash and other unchanging code again when requesting the page, which will degrade the user experience. Modularized unpacking will pack these invariant dependencies into a new JS file, so that users will not request echarts, XLSX, loDash and other invariant code again every time the package is released.

Code changes

Webpack-bundle-analyzer visually displays packages referenced by packaged JS

npm i webpack-bundle-analyzer -D
Copy the code

Introduce Echarts, XLSX, loDash into the project

npm i echarts xlsx lodash -S
Copy the code

app.js

import "regenerator-runtime/runtime";
import _ from 'lodash';
import echarts from 'echarts';
import xlsx from 'xlsx';
console.log(echarts)
console.log(xlsx);
document.getElementById('app'). InnerHTML = _. Ceil (2 and 4);Copy the code

Webpack.base.conf.js configuration visualization now packages files.

var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

  plugins:[
    new BundleAnalyzerPlugin(),
    new htmlWebpackPlugin({
      filename:'index.html',
      template:'./index.html',
      inject:trueCollapseWhitespace: isPord, // Delete whitespace and newlines minifyCSS: // collapseWhitespace: isPord, // IsPord // compression inline CSS},})],Copy the code

Run the package command

npm run build

Copy the code


The default configuration

Optimization.splitchunks is a new feature of webpack4 that does code unpacking by default.

chunks:

  • All: Regardless of whether the file is introduced asynchronously or synchronously, splitChunks can be used for code unpacking.
  • 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 Specifies the list of rules for splitting packages

Test can configure regex and write function as a packing rule. Other attributes can inherit splitChunks.

minChunks

Depends on the minimum number of times to be introduced before unpacking.

Simple Configuration Modification

Copy the default configuration and change chunks to ‘all’.

    optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true}}}},Copy the code

Run the package command

npm run build
Copy the code

Unpacking again

Vendors ~app.js is a 1.71MB file, too big, so keep unpacking. Add a new unpacking rule

CacheGroups :{echarts:{// New ungroup rule name:'echarts'// rule name chunks:'all'Priority :10, // The priority of this rule, for example, when unpacking in webpack, // echarts packages will match the rule with the highest priority first, if this rule is met, // Import code into this rule, not into the following rule.test:/(echarts)/, // regular matching rule minChunks:1 // this rule can be used if it is introduced at least once in the code. }, vendors: {test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true}}Copy the code

Run the package command

npm run build
Copy the code



The official demo

Modular Unpacking 2

See the Demo9 section for asynchronous loading using only the default configuration of splitChunks to understand the benefits of asynchronous loading. . Use React as a development framework.

Synchronized imported unpacking

Redux -> 16.8.6 react-dom -> 16.8.6 react-router-dom -> 5.0.0Copy the code

Install the react

npm i react react-router-dom react-dom -S
Copy the code

Parsing JSX code also requires @babel/ preth-React

npm i @babel/preset-react -D
Copy the code

See Demo9 for the code details. The two routes correspond to different components.

    import About from './src/view/about';
    import Inbox from './src/view/inbox'; / /... <App> <Route path="/about" component={About} />
        <Route path="/inbox" component={Inbox} />
    </App>
Copy the code

Inbox.js introduced and used Echarts.

import React from 'react';
import echarts from 'echarts';
class Inbox extends React.Component {
  componentDidMount() {
    var myChart = echarts.init(document.getElementById('main'));
    var option = {
      tooltip: {
        show: true
      },
      legend: {
        data: ['sales']
      },
      xAxis: [
        {
          type: 'category',
          data: ["Shirt".Cardigan 2."Chiffon 222".111 "pants"."High heels"."Socks"]
        }
      ],
      yAxis: [
        {
          type: 'value'
        }
      ],
      series: [
        {
          "name": "Sales"."type": "bar"."data": [5, 20, 40, 10, 10, 20]}]}; // Load data for echarts mychart.setoption (option); }render() {
    return (
      <div>
        <h2>Inbox</h2>
        <div style={{ height: '400px' }} id="main"></div>
      </div>
    )
  }
}
export default Inbox;
Copy the code

SplitChunks use the default configuration

    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true}}}Copy the code

Run the package command

npm run build
Copy the code

It was already indicated when packaging that the packaged code was too bad for performance. You can use import() or require to limit package size. Make sure that some parts of the application are lazily loaded.

WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
Copy the code

When we are on the home page, there is no/INBOX route, but the code in the/INBOX route is packaged in the app.js js. The first screen rendering is very slow. Dynamic introduction of components is required according to the official prompt. The code of corresponding components is loaded only when the route is switched to/INBOX.

Asynchronous import and unpack

The asynchronous import component requires a plug-in for Babel, @babel/plugin-syntax-dynamic-impor.

{
	test: /\.(js|jsx)? $/, // regular matches, all.js files are compiled using this rule exclude: /node_modules/, // exclude folders. Loader does not translate files in this folder:"babel-loader"{// presets: [// interpreter configuration [// interpreter configuration ["@babel/preset-env", {
					useBuiltIns: "usage",
					corejs: 3
				},
			],
			["@babel/preset-react"]
		],
		plugins: ["@babel/plugin-syntax-dynamic-import"] // Translate plug-in configuration}},Copy the code

New AsyncComponent. Js.

import React, { Component } from "react";

export default function asyncComponent(importComponent) {
  class AsyncComponent extends Component {
    constructor(props) {
      super(props);

      this.state = {
        component: null
      };
    }

    async componentDidMount() { const { default: component } = await importComponent(); // Render component this.setState({component: component}); }render() {
      const C = this.state.component;

      return C ? <C {...this.props} /> : null;
    }
  }

  return AsyncComponent;
}
Copy the code

Change the lead-in mode

//import Inbox from './src/view/inbox';
const Inbox = asyncComponent(() => import('./src/view/inbox'));
Copy the code

Run the package command

npm run build
Copy the code

npm run server
Copy the code

The home page is not loaded with 1.js

React’s dynamic import component works fine. You can also customize the chunk name according to the official document

const Inbox = asyncComponent(() => import(/* webpackChunkName: "echarts"* /'./src/view/inbox'));
Copy the code

Dependency packages can also be imported asynchronously

async componentDidMount() {
    const {default:echarts}= await import('echarts');
    var myChart = echarts.init(document.getElementById('main'));
}
Copy the code

conclusion

  • Splitchunks. chunks: ‘all’ is recommended, and other parameters remain unchanged. ‘all’ means that both synchronous import and asynchronous import can be unpacked.
  • Introduce as few plug-ins as you need, for example Echarts officially provides a method of loading on demand.
  • When a plug-in is not available on the home page, try to use asynchronous loading to introduce it.

JS Tree Shaking

Reference code Demo10

Tree Shaking

Tree jitter is a term commonly used in JavaScript context to eliminate dead code. It relies on the static structure of the ES2015 module syntax, namely imports and exports. The names and concepts have been summarized and promoted by the ES2015 module bundler.

Shake the tree. Webpack4.0 automatically removes unwanted js when packing. Of course you need development to work with it: Load on demand let’s take LoDash as an example. To get a sense of how much code we’ve shaken out, let’s unpack the React package and pack it separately.

splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        reactVendors: {
          chunks: 'all'.test: /(react|react-dom|react-dom-router)/,
          priority: 100,
          name: 'react',
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true}}}Copy the code

Use LoDash in about.js

import React from 'react';
import _ from 'lodash';
class About extends React.Component {

    render() {const s = _. Concat ([1], [2,3,1,2,3,4,6,78,41]);return (
        <h3>{s}</h3>
      )
    }
  }
  export default About;
Copy the code

Run the package command

npm run build
Copy the code

lodash-es

npm i lodash-es -S
Copy the code

Change the about.js code

import React from 'react';
//import _ from 'lodash';
import { concat } from 'lodash-es';
class About extends React.Component {

    render() {const s = concat([1],[2,3,1,2,3,4,6,78,41]);return (
        <h3>{s}</h3>
      )
    }
  }
  export default About;
Copy the code

Run the package command

npm run build
Copy the code

Gzip compression.

Benefits of Gzip compression.

Open the nuggets home page. Find a random JS resource.

Content-encoding in Response Headers represents the resource transfer format. Note This resource uses a Gzip file. Many companies use Gzip for resource transfer, because code packaged as Gzip for transfer requires very little resource.

Gzip transport is something that the server needs to configure. Even if our upload code is not Gzip compressed, the server is configured to Gzip the resources and transfer them. For example, the Gzip configuration of Nginx.

Gzip configuration is all about server configuration. What do we do? The server actually looks for a gzip file for the resource, and if it doesn’t have one, it gzip compresses and transfers the resource. This gives you extra time to compress. If we generate gzip files through Webpack, we can reduce the time for the server to generate gzip files and reduce the server stress.

Js package compressed configurationoptimization.minimiz

The value of this property can be Boolean or array.

When the property value is Boolean

Minimiz defaults to true when mode is prduction. Automatic compression using the Terser-webpack-plugin.

When the property value is an array


Configure Gzip compression

The default configuration is not changed. Compression plugin compression-webpack-plugin is used.

npm i  compression-webpack-plugin -D
Copy the code

The configuration optimization. Minimiz

minimizer: [
      new CompressionPlugin({
        filename: '[path].gz[query]'[path]. Gz [query] is the default name algorithm:'gzip'// Compression algorithmtest: / \. (js | | | HTML CSS SVG) $/, / / matching resources compressionOptions: {8} level:, / / compression level 9 default threshold: MinRatio: 0.8, // Only deal with resources that compress better than this ratio (minRatio = compressed size/raw size). // Example: You have a 1024B image.png file, and the compressed version is 768B, so minRatio equals 0.75. In other words, when the compressed size/raw size value is less than the minRatio value, // will process the resource. The default is 0.8. deleteOriginalAssets:false// Whether to delete the original asset. }), new TerserPlugin({ parallel:true,}),]Copy the code

npm run build
Copy the code

.zip