This paper is participating in thePerformance optimization combat record”Topic essay activity

preface

Hi everyone, I’m YK bacteria, front-end dish chicken. Most of my blog posts are based on notes, but I also have some summaries of my own, and this one is the same. In fact, this is also my notes a long time ago, today to share with you, is also B station still silicon Valley’s latest webpack video part of the notes. This article has been in my nuggets draft box, was also ready to take out when small knowledge hair, just nuggets to performance optimization activities, catch a coincidence, hair this ~ sorry everybody, I water water water water an activity article ~

BB two sentences again

Every time I watch some videos, I will blog my notes, and then leave a link of my notes in the comments section. I have been taking notes on another platform before, so I transfer them. Basically, I call all platforms YK bacteria. Where to write bad place welcome message criticism and correction.


The text start

Last time we briefly introduced the basic use of WebPack, today we will take a look at some configuration using WebPack for performance optimization, although this article only briefly listed some concepts and basic configuration use, but will give you a general understanding of performance optimization, later browsing the documentation will be much easier

1. What is WebPack performance optimization

  • Development environment performance optimization
  • Production environment performance optimization

1.1 Development environment performance optimization

  • Optimize packaging build speed
    • HMR
  • Optimized code debugging
    • source-map

1.2 Optimization of production environment performance

  • Optimize packaging build speed
    • oneOf
    • Babel cache
    • Multi-process packaging
    • externals
    • dll
  • Optimize the performance of code execution
    • Cache (hash – chunkhash – contenthash)
    • tree shaking
    • code split
    • Lazy loading/preloading
    • pwa

2. Development environment performance optimization

2.1 HMR

Hot Module replacement Hot module replacement

target: "web".devServer: {
  // Enable HMR
  // When the WebPack configuration is changed, the webpack service must be restarted for the new configuration to take effect
  hot: true,}Copy the code
  • What it does: When a module changes, it repackages only that module (not all modules) [greatly improves build speed]
  1. Style files: you can use the HMR function because style-loader is implemented internally

  2. Js file: HMR function cannot be used by default –> Need to modify js code, add code supporting HMR function

Note: the HMR function can only handle other files that are not entry JS files.

Add it to index.js

if (module.hot) {
  // Once module.hot is true, HMR is enabled. --> Make the HMR function code work
  module.hot.accept("./print.js".function () {
    // The print.js method listens for changes in the print.js file, and when changes occur, other modules are not repackaged and built.
    // The following callback functions are executed
    print();
  });
}
Copy the code
  1. HTML files: the HMR function is disabled by default. It also causes problems: HTML files cannot be hot updated.

[“./ SRC /js/index.js”, “./ SRC /index.html”]

2.2 the source – the map

Source-map: a technique that provides source-to-build-code mapping (mapping to trace source code errors if errors are made after a build)

devtool: 'source-map'
Copy the code

The options behind devtool can be filled with:

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
Copy the code
code location note
source-map external Error code exact information and source code error location
inline-source-map inline Only one inline source-map is generated

Error code exact information and source code error location
hidden-source-map external Error code error cause, but no error location

You cannot trace source code errors, only the error location of the built code
eval-source-map inline Each file generates a corresponding source-map, all in eval

Error code exact information and source code error location
nosources-source-map external Error code exact information, but no source code information
cheap-source-map external Error code exact information and source code error location

Only accurate lines
cheap-module-source-map external Error code exact information and source code error location

Module adds the Loader source map

The difference between inline and external

  1. External generated file, inline not
  2. Inline builds are faster

[Development environment] : Fast speed, more friendly debugging

  1. Speed (eval > the inline > being >…).
  • eval-cheap-souce-map
  • eval-source-map
  1. More debug friendly
  • souce-map
  • cheap-module-souce-map
  • cheap-source-map
  1. Recommended use –>eval-source-map / eval-cheap-module-souce-map

[Production environment] : Should source code be hidden? Debugging should be more friendly

Inlining makes code bigger, so it’s not used in production

Nosource-source-map Is hidden

Hidden-source-map only hides the source code, prompting post-build code error messages

Source-map/cheap-module-souce-map is recommended

3. Optimized production environment performance

3.1 oneOf

Only one loader will match after using oneOf

Note: you can’t have two configurations that handle the same type of file, js needs to bring esLint out

module: {
  rules: [{// In package.json eslintConfig --> Airbnb
      test: /\.js$/,
      exclude: /node_modules/.// Priority execution
      enforce: "pre".loader: "eslint-loader".options: {
        fix: true,}}, {// The following loader will only match one
      // Note: No two configurations can handle the same type of file
      oneOf: [{test: /\.css$/,
          use: [...commonCssLoader],
        },
        {
          test: /\.less$/,
          use: [...commonCssLoader, "less-loader"],},/* Normally, only one loader can process a file. When a file is to be processed by multiple Loaders, it is important to specify the order in which loader executes: esLint before Babel */
        {
          test: /\.js$/,
          exclude: /node_modules/,
          loader: "babel-loader".options: {
            presets: [["@babel/preset-env",
                {
                  useBuiltIns: "usage".corejs: { version: 3 },
                  targets: {
                    chrome: "60".firefox: "50",},},],],},}, {test: /\.(jpg|png|gif)/,
          loader: "url-loader".options: {
            limit: 8 * 1024.name: "[hash:10].[ext]".outputPath: "imgs".esModule: false,}}, {test: /\.html$/,
          loader: "html-loader"}, {exclude: /\.(js|css|less|html|jpg|png|gif)/,
          loader: "file-loader".options: {
            outputPath: "media",},},],},Copy the code

3.2 the cache

3.2.1 Babel cache

Add the configuration cacheDirectory: true to options in babel-loader to make the second package build faster

{
  test: /\.js$/,
  exclude: /node_modules/,
  loader: "babel-loader".options: {
    presets: [["@babel/preset-env",
        {
          useBuiltIns: "usage".corejs: { version: 3 },
          targets: {
            chrome: "60".firefox: "50",},},],// Enable the Babel cache
    // On the second build, the previous cache is read
    cacheDirectory: true,}}Copy the code

3.2.2 File Resource Caching

Forced cache, in a certain period of time always go to the cache, but temporarily change a resource, resulting in page update is not timely, with the cache, so to package out of the file name behind a hash value, so change the file content, the file name will change

Hash: A unique hash value is generated for each Wepack build

  • Problem: Because JS and CSS use the same hash value
  • If repackaged, it invalidates all caches (maybe I only changed one file)

Chunkhash: Hash value generated by chunk. If the package comes from the same chunk, the hash value is the same

  • Problem: JS and CSS have the same hash value
  • Because CSS was introduced in JS, it belongs to the samechunk

Contenthash: Generates hash values based on file contents

  • Different files must have different hash values
  • Make the code run online and cache easier to use
output: {
  filename: "js/built.[contenthash:10].js".path: resolve(__dirname, "build"),},plugins: [
  new MiniCssExtractPlugin({
    filename: "css/built.[contenthash:10].css",})]Copy the code

3.3 the tree shaking

Tree shaking: Remove useless code

  • Prerequisites: 1. ES6 modularization must be used. 2
  • Effect: Reduces code size

Configure in package.json

  • “SideEffects “: false all code has no sideEffects
  • Problem: CSS / @babel/polyfill files may dry out
  • Solution: sideEffects: [“.css”, “.less”]

3.4 code split

Split code: Split a large file into multiple files [load on demand]

Way one more entrance

Test.js is no longer needed in index.js

/ / a single entrance
// entry: './src/js/index.js',
entry: {
  // Multiple entries: There is one entry, and the final output has a bundle
  index: "./src/js/index.js".test: "./src/js/test.js",},output: {
  // [name] : Indicates the name of the file
  filename: "js/[name].[contenthash:10].js".path: resolve(__dirname, "build"),}Copy the code

Methods two optimization

Add a configuration item to package the node_modules code as a separate chunk of final output

/* 1. The node_modules code can be packaged as a separate chunk of the final output 2. Automatic analysis of multi-entry chunks, whether there are public files. If so, it will be packaged into a single chunk */
optimization: {
  splitChunks: {
    chunks: 'all'}}Copy the code

For example, I’ve introduced jquery (which would otherwise be merged with index.js) to automatically analyze multiple entry chunks for common files. If there is, it will be packaged into a single chunk

It outputs three chunks

Mode three JS code dynamic loading

Chunk import dynamic import syntax: it is possible to package a file separately by using JS code

Test.js is introduced in index.js

/* Dynamic import syntax: it is possible to package a file separately */
import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) = > {
    // The file is successfully loaded ~
    console.log(mul(2.5));
  })
  .catch(() = > {
    console.log('File load failed ~');
  });
Copy the code

3.5 Lazy loading and preloading

Example: clicking a page button to call a JS file should load the js file after clicking

Each JS file is introduced only after the button is clicked so that lazy loading can be achieved

  • Lazy loading: files are loaded when they are needed

  • Prefetch: js files will be loaded before they are used.

  • Normal loading can be considered parallel loading (loading multiple files at the same time)

  • Prefetch: Surreptitiously loads resources after other resources have loaded and the browser is free

document.getElementById('btn').onclick = function() {
  // Lazy loading ~ : files are loaded only when they are needed
  // Prefetch: preloads the JS file before it is used
  // Normal loading can be considered parallel loading (multiple files are loaded at the same time)
  // Prefetch: Wait until other resources are loaded and the browser is free, then secretly load the resources
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) = > {
    console.log(mul(4.5));
  });
};
Copy the code

3.6 PWA

PWA: Progressive Web development application (offline access)

workbox –> workbox-webpack-plugin

npm i workbox-webpack-plugin

  1. Helps ServiceWorker start quickly
  2. Delete the old ServiceWorker
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

module.exports = {
  plugins: [
    new WorkboxWebpackPlugin.GenerateSW({
      2. Delete the old ServiceWorker and generate a Serviceworker profile ~ */
      clientsClaim: true.skipWaiting: true}})]Copy the code

Then configure it in the entry file index.js

/ / register serviceWorker
// Handle compatibility issues if supported, if not not used
if ('serviceWorker' in navigator) {
  window.addEventListener('load'.() = > {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() = > {
        console.log('Sw registered successfully ~');
      })
      .catch(() = > {
        console.log('SW registration failed ~');
      });
  });
}
Copy the code
  1. Eslint does not recognize window, navigator global variables

    Solution: eslintConfig in package.json needs to be modified

"env": {
  "browser": true Browser-side global variables are supported
}
Copy the code
  1. The SW code must run on the server

    –> nodejs

    –> NPM I serve-g serve-s build Start the server and expose all resources in the build directory as static resources

This way, after the first load, you can cache it, and you can still access the page content after you go offline

3.7 Packaging Multiple Processes

npm i thread-loader
Copy the code

Enable multi-process packaging.

Process startup takes about 600ms and process communication is also overhead. Only when the work consumes a long time, it is necessary to package the multi-process.

{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
    /* Enable multi-process packaging. Process startup takes about 600ms and process communication is also overhead. Multi-process packaging is required only if the work takes a long time
    // "thread-loader",
    {
      loader: "thread-loader".options: {
        workers: 2.// Process 2}, {},loader: "babel-loader".options: {
        presets: [["@babel/preset-env",
            {
              useBuiltIns: "usage".corejs: { version: 3 },
              targets: {
                chrome: "60".firefox: "50",},},],// Enable the Babel cache
        // On the second build, the previous cache is read
        cacheDirectory: true,},},],}Copy the code

3.8 externals

Prevents packages from being packaged and exported to the final bundle

externals: {
  // Refuse to package jQuery
  jquery: 'jQuery'
}
Copy the code

Jquery is introduced via CDN links in index.html

<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
Copy the code

3.9 DLL

Use DLL technology for some libraries (third-party libraries: jquery, React, vue…) Pack separately

DLL is packaged once integrated together, later do not need to be packaged.

CDN externals (externals)

webpack.dll.js

const { resolve } = require("path");
const webpack = require("webpack");

module.exports = {
  entry: {
    [name] --> jquery
    // ['jquery'] --> The library is jquery
    jquery: ["jquery"],},output: {
    filename: "[name].js".path: resolve(__dirname, "dll"),
    library: "[name]_[hash]".// What is the name of the contents exposed in the packaged library
  },
  Jquery_hash = jquery_hash = jquery_hash = jquery.js
  plugins: [
    // Package to generate a manifest.json --> provide and jquery mapping
    new webpack.DllPlugin({
      name: "[name]_[hash]".// The exposed content name of the mapping library
      path: resolve(__dirname, "dll/manifest.json"), // Output file path}),].mode: "production"};Copy the code

When you run Webpack, the default is to look for the webpack.config.js configuration file

Requirements: You need to run the webpack.dlL. js file

–> webpack --config webpack.dll.js

webpack.config.js

webpack.DllReferencePlugin

Tell WebPack which libraries are not included in the package and change their names when used

Download the plugin

npm i add-asset-html-webpack-plugin

Package a file and export it, and automatically import the resource in HTML

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

module.exports = {
  entry: './src/index.js'.output: {
    filename: 'built.js'.path: resolve(__dirname, 'build')},plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    // Tell Webpack which libraries are not included in the package, and change their names when used
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')}),// Package a file and import the resource automatically in HTML
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')})],mode: 'production'
};
Copy the code

4. To summarize

Review the

Development environment performance optimization

  • Optimize packaging build speed
    • HMR
  • Optimized code debugging
    • source-map

Production environment performance optimization

  • Optimize packaging build speed
    • oneOf
    • Babel cache
    • Multi-process packaging
    • externals
    • dll
  • Optimize the performance of code execution
    • Cache (hash – chunkhash – contenthash)
    • tree shaking
    • code split
    • Lazy loading/preloading
    • pwa