In the previous chapter, we learned all the common configurations for WebPack

In this section we will take a look at how to implement optimizations in WebPack by first writing the most basic WebPack configuration and then implementing the various optimizations in turn!

Remove unnecessary Css styles

Let’s start with the code we wrote

import './style.css'
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<div>hello</div>.document.getElementById('root'));
Copy the code
body{
    background: red
}
.class1{
    background: red
}
Copy the code

The. Class1 file here is obviously useless, and we can search the files in the SRC directory to remove the useless styles

Purgecss can remove unused CSS, Paths are absolute paths NPM i-d purgecss-webpack-plugin mini-css-extract-plugin glob webpack.config.js + const glob = require('glob'); + const PurgecssPlugin = require('purgecss-webpack-plugin'); { test: /\.css/, include: path.resolve(__dirname,'src'), exclude: /node_modules/, use: [{ loader: MiniCssExtractPlugin.loader },'css-loader'] } module.exports = { mode: 'development', plugins: [ new PurgecssPlugin({ paths: glob.sync(`${path.join(__dirname,"src")}/ * * /*`, { nodir: true }) // Does not match directories, only files
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename:'[id].css'
    })
  ]
}

Copy the code

Note that there may be side effects and the effect is not particularly noticeable

Image compression plug-in

Optimize the packaged images

npm install image-webpack-loader --save-dev
Copy the code

Use the compressed image plug-in before file-loader

loader: "image-webpack-loader".options: {
  mozjpeg: {
    progressive: true.quality: 65
  },
  // optipng.enabled: false will disable optipng
  optipng: {
    enabled: false,},pngquant: {
    quality: [0.90.0.95].speed: 4
  },
  gifsicle: {
    interlaced: false,},// the webp option will enable WEBP
  webp: {
    quality: 75}}Copy the code

You can find that there is a significant change in the size of the picture

Tree-shaking && Scope-Hoisting

Tree-shaking

As the name suggests, shake out the useless content. Look at the following code

main.js

import { minus } from "./calc";
console.log(minus(1.1));
Copy the code

calc.js

import {test} from './test';
export const sum = (a, b) = > {
  return a + b + 'sum';
};
export const minus = (a, b) = > {
  return a - b + 'minus';
};
Copy the code

test.js

export const test = () = >{
    console.log('hello')}console.log(test());
Copy the code

Observe the above code in fact we mainly use minus method,test.js code has side effects!

The default mode:production is tree-shaking automatically, but ‘hello’ is still printed when packaged, so we need to configure it not to use side effects

Configure in package.json

"sideEffects":false.Copy the code

If set to this, the CSS file will not be imported by default, because we also import CSS by importing ‘./style.css’.

Tree-shaking is mainly for ES6 modules. We can import CSS using the require syntax, but this is a bit out of place, so we can configure CSS files not as a side effect

"sideEffects": ["**/*.css"
]
Copy the code

The default tree-shaking does not take effect in a development environment. You can configure the logo prompt

optimization:{
  usedExports:true 
}
Copy the code

4.2 the Scope Hoisting

Scope enhancement can reduce code size and save memory

let a = 1;
let b = 2;
let c = 3;
let d = a+b+c
export default d;
/ / introduce d
import d from './d';
console.log(d)
Copy the code

The final result will be console.log(6)

  • The amount of code is significantly reduced
  • Reducing the number of functions also reduces the memory footprint

5.DllPlugin && DllReferencePlugin

Each time the third-party module needs to be rebuilt, which consumes a lot of performance. We can package the third-party library as a dynamically linked library first, and then only need to find the built library in the future, which can greatly save the construction time

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<h1>hello</h1>.document.getElementById('root'))
Copy the code

DllPlugin

React and react-dom can be packaged separately

Package separately to create webpack.dlL.js

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
    entry: ['react'.'react-dom'].mode:'production'.output: {filename:'react.dll.js'.path:path.resolve(__dirname,'dll'),
        library:'react'
    },
    plugins: [new DllPlugin({
            name:'react'.path:path.resolve(__dirname,'dll/manifest.json')]}})Copy the code

Execute “webpack – config webpack. DLL. Js command, you can see the DLL directory created two files are the manifest. Json, react. DLL. Js

We’ll load the modules in the react.dll. Js file via manifest.json

DllReferencePlugin

We can reference the dynamic link library we just packaged in our project

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
// The contents of the dynamic link library will be referenced in the build
new DllReferencePlugin({
  manifest:path.resolve(__dirname,'dll/manifest.json')}),// You need to manually import react.dll
new AddAssetHtmlWebpackPlugin(
  { filepath: path.resolve(__dirname,'dll/react.dll.js')})Copy the code

Using the DllPlugin can greatly speed up builds

Note that in webpack5, this scheme has been removed

Dynamic loading

Dynamic load files after click

let btn = document.createElement('button');
btn.innerHTML = 'Click to load video';
btn.addEventListener('click'.() = >{
    import('./video').then(res= >{
        console.log(res.default);
    });
});
document.body.appendChild(btn);
Copy the code

Add names to dynamically imported files

output:{
  chunkFilename:'[name].min.js'
}
import(/* webpackChunkName: "video" */ './video').then(res= >{
    console.log(res.default);
})
Copy the code

The resulting file is video.min.js

Package the file analysis tool

Install the webpack-bundle-Analyzer plug-in

npm install --save-dev webpack-bundle-analyzer
Copy the code

The use of plug-in

const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); mode ! = ="development" && new BundleAnalyzerPlugin()
Copy the code

By default, an analysis chart of the current application is displayed

SplitChunks

Let’s take a look at SplitChunks, which can be used to separate third-party modules and public modules at compile time

Configure the project as a multi-entry file

entry:{
  a:'./src/a.js'.b:'./src/b.js'
}
Copy the code

Let’s make both modules A and B reference jquery. Don’t forget to remove the externals configuration

Configure the SplitChunks plug-in

The default configuration is here, and I’ll describe it one by one

splitChunks: {
  chunks: 'async'.// Split the asynchronous module
  minSize: 30000.// The minimum size of the split file
  maxSize: 0.minChunks: 1.// Number of references
  maxAsyncRequests: 5.// Maximum number of asynchronous requests
  maxInitialRequests: 3.// Maximum number of initial requests
  automaticNameDelimiter: '~'.// Extract the named delimiter
  automaticNameMaxLength: 30.// The maximum length of the name
  name: true.cacheGroups: { / / cache group
    vendors: { // Remove the third party first
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: { 
      minChunks: 2.priority: -20./ / priority
      reuseExistingChunk: true}}}Copy the code

Let’s change async to initial

We are importing the LoDash library dynamically for each file and changing it to Async

import('lodash')
Copy the code

Introduce C.js for each entry, and modify the configuration file

splitChunks: {
  chunks: 'all'.name: true.cacheGroups: {
    vendors: {
      test: /[\\/]node_modules[\\/]/,
      priority: -10
    },
    default: {
      minSize:1.// It is not a third party module, it will be removed even if it is introduced twice
      minChunks: 2.priority: -20,}}}Copy the code

In this way, the parameters of the chunks will be clear. Hot update

Hmr-hot Module Replacement is one of the most useful features webPack provides. It allows various modules to be replaced, added, and removed at run time without needing a complete refresh to reload the entire page

  • Preserve the state of the application that was lost when the page was fully reloaded
  • Only update what has changed to save development time
  • Adjusting styles is much faster, almost the same as changing styles in the browser debugger

Hot updates are enabled. The default style can support hot updates or force refresh if not

devServer:{
  hot:true
}
new webpack.NamedModulesPlugin(),
Copy the code

Make JS support hot updates

import sum from './sum';
console.log(sum(1.2));
if(module.hot){ // If hot update is supported
    module.hot.accept(); // Re-execute the current entry file when the entry file changes
}
Copy the code

IgnorePlugin

Ignore the import and require syntax

new webpack.IgnorePlugin(/^\.\/locale$/./moment$/)
Copy the code

Time consuming analysis

You can calculate the running speed of each step

const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
  module.exports =smw.wrap({
});
Copy the code

noParse

Module. noParse, for dependent libraries like JQ, there is no internal reference to other libraries, so there is no need to parse when we package, which can increase the packaging speed

noParse:/jquery/
Copy the code

resolve

  • Reduce the number of files to be processed
  • Narrow down the search

Narrow down the search.

extensions

  • You can specify extensions without adding file extensions to req©uire or import
  • The search in turn tries to add extensions to match

alias

  • Configuring aliases speeds up WebPack’s search for modules
  • Whenever the bootstrap module is imported, it will import bootstrap directly instead of looking up the module’s lookup rules from the node_modules folder
resolve: {
  extensions: [".js".".jsx".".json".".css"].alias: {},modules: ['node_modules']},Copy the code

include/exclude

When using loader, you can specify which files do not pass loader or which files pass Loader

{
  test: /\.js$/,
  use: "babel-loader".// include:path.resolve(__dirname,'src'),
  exclude:/node_modules/
},
Copy the code

happypack

Multithreaded packaging allows us to delegate different logic to different threads for processing

npm install --save-dev happypack
Copy the code

The use of plug-in

const HappyPack = require('happypack');
rules:[
  {
    test: /\.js$/,
    use: 'happypack/loader? id=jsx'
  },

  {
    test: /\.less$/,
    use: 'happypack/loader? id=styles'},]new HappyPack({
  id: 'jsx'.threads: 4.loaders: [ 'babel-loader']}),new HappyPack({
  id: 'styles'.threads: 2.loaders: [ 'style-loader'.'css-loader'.'less-loader']})Copy the code

Use of preload and prefetch

Preload

  • Preload is usually used for key resources used in the page, including key JS, fonts, and CSS files
  • Preload will increase the load order weight of resources, so that key data can be downloaded in advance, optimize the speed of page opening
  • By adding a preloaded comment to a resource, you indicate that the module needs to be used immediately
  • The load priority of a resource is divided into five levels, namely
    • The Highest Highest
    • High High
    • Medium Medium
    • Low, Low
    • Lowest minimum
  • Asynchronous/delayed/inserted scripts (regardless of location) are Low in network priority

<link rel="preload" as="script" href="utils.js">

import(
  `./utils.js`
  /* webpackPreload: true */
  /* webpackChunkName: "utils" */
)

Copy the code

Prefetch

  • Different from preload, prefetch tells the browser about a resource that is likely to be used in the future, and the browser will load the corresponding resource when it is free. If the user’s behavior can be predicted, such as lazy loading or clicking on another page, it is equivalent to preloading the required resource in advance
<link rel="prefetch" href="utils.js" as="script">

button.addEventListener('click', () => {
  import(
    `./utils.js`
    /* webpackPrefetch: true */
    /* webpackChunkName: "utils" */).then(result => { result.default.log('hello'); })});Copy the code

preload vs prefetch

  • Preload is the resource that tells the browser that the page must need and that the browser must load
  • Prefetch, on the other hand, tells the browser what resources the page may need and the browser may not load them
  • So a recommendation: Use Preload for resources that are necessary for the current page and Prefetch for resources that may be used in future pages

Using the cache

  • There are several general ideas for using caching in Webpack:
    • Babel-loader enables caching
    • Use the cache – loader
    • Use the hard – source – webpack – the plugin

babel-loader

  • Babel has high performance consumption in the process of escaping JS files. It caches the results of babel-loader execution, and tries to read the cache when repackaging the build, thus improving the packaging build speed and reducing the consumption

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [{
      loader: "babel-loader",
      options: {
        cacheDirectory: true}}},Copy the code

cache-loader

  • Add this loader before some of the more performance expensive loaders to cache the results to disk
  • Storing and reading these cache files can take some time, so use this loader only for performance heavy loaders

cnpm i cache-loader -D

const loaders = ['babel-loader'];
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          'cache-loader',
          ...loaders
        ],
        include: path.resolve('src')
      }
    ]
  }
}
Copy the code

Log beautification

  • Friendly-errors-webpack-plugin identifies certain categories of Webpack errors and cleans, aggregates, and prioritizes them to provide a better developer experience

The installation

cnpm i friendly-errors-webpack-plugin node-notifier -D

2.12. webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const notifier = require('node-notifier');
const ICON = path.join(__dirname, 'icon.jpg');
module.exports = {
  mode: "development",
  devtool: 'source-map',
  context: process.cwd(),
  entry: {
    main: "./src/index.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.js"
  },
  plugins:[
    new HtmlWebpackPlugin(),
    new FriendlyErrorsWebpackPlugin({
      onErrors: (severity, errors) => {
        const error = errors[0];
        notifier.notify({
          title: "Webpack compilation failed", message: severity + ': ' + error.name, subtitle: error.file || '', icon: ICON }); }}})];Copy the code

Velocity analysis

  • Speed-measure -webpack5-plugin Analyzes the packaging speed

The installation

cnpm i speed-measure-webpack5-plugin -D

webpack.config.js

const SpeedMeasureWebpackPlugin = require('speed-measure-webpack5-plugin');
const smw = new SpeedMeasureWebpackPlugin();
module.exports = smw.wrap({
  mode: "development",
  devtool: 'source-map',
  ...
});
Copy the code

File volume monitoring

  • Webpack-bundle-analyzer is a webpack plug-in that needs to be used with WebPack and webpack-CLI. The function of this plug-in is to generate code analysis reports to help improve code quality and website performance
  • It can intuitively analyze what the packaged file contains, how the size ratio, module contains relations, dependencies, whether the file is repeated, how the size after compression, for these, we can carry out file segmentation and other operations.

The installation

cnpm i webpack-bundle-analyzer -D

Compile the start

webpack.config.js
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')
module.exports={
  plugins: [
    new BundleAnalyzerPlugin() 
  ]
}
Copy the code

Separate start

webpack.config.js
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')
module.exports={
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'disabled', // Do not start the HTTP server that displays packaged reports
      generateStatsFile: true.// Whether to generate stats.json file]}}),"scripts": {
  "build": "webpack"."start": "webpack serve"."dev":"webpack --progress"."analyzer": "webpack-bundle-analyzer --port 8888 ./dist/stats.json"
}
Copy the code