preface

In the previous Webpack tutorial, we talked about the basic build of the webpage packaging project, which tells you how to compress JS and CSS files. This can actually be seen as part of the volume optimization of the project, which is further studied below.

Note: The following features are based on Webpack version ^4.43.0 debugging.

The quantitative analysis of

In the base build, we combined project code and third-party code into a bundle. If we need to optimize this file, the idea should be to delete and merge the modules in it (duplicate content).

So, first we need to know the size of each bundle template. In this case, we need to use the plugin Webpack-Bundle-Analyzer, which outputs an interactive visual tree showing the size of the bundle.

Note: Can also be usedwebpack --profile --json > stats.jsonCommand (not recommended for this scheme) in the generatedstats.jsonIn the viewbundleFile size can also be inOfficial analysis toolsAnalysis in

// webpack.prod.config.js
const baseWebpackConfig = require('./webpack.base.config');

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

constwebpackConfig = merge(baseWebpackConfig, { ... .plugins: [
    newBundleAnalyzerPlugin(), ... ,]})module.exports = webpackConfig;
Copy the code

This way, when we executenpm run buildAfter the command is executed, it is openedhttp://127.0.0.1:8888/:

In this way, we can intuitively analyze which file has the largest volume and which module has the largest volume.

File compression

When we need to optimize volume processing, the initial thought is usually to compress files. In this project, in addition to using gzip to compress files, Webpage also compresses JS, CSS, and HTML files. (Refer to the basics of development in the previous article.)

  • Js file, SettingsmodeThe value ofproductionWebpage will automatically compress
  • The CSS file, use the plug-in CssMinimizerWebpackPlugin
  • HTML file, SettingsmodeThe value ofproductionWebpage will automatically compress
  • Images, using the image-webpack-loader plug-in automatically compressed images

Resolution of the code

CDN

In real projects, we can see that third-party libraries account for a large proportion of the resulting bundle files. We can configure externals to extract the third library commonly used in it (such as Vue, VUe-Router, vuex, etc.), place it in the CDN, and introduce it through

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>
Copy the code
// webpack.prod.config.js
module.exports = { ... .externals: {
    // jquery, where key is the name of the third-party library introduced in the project logic code
    // jQuery, for value, represents global variables introduced through script
    jquery: 'jQuery'}};// Project logic code
import $ from 'jquery'; .Copy the code

To prevent the repeat

The SplitChunksPlugin plug-in can extract a common dependency module into an existing Entry chunk or into a newly generated chunk.

Webpack will automatically split code blocks based on the following criteria:

  • New code blocks are shared or come from the node_modules folder
  • New code block larger than 30KB (before min+giz)
  • The number of requests to load code blocks on demand should be <=5

That is, no more than five requests can be sent to load chunk.

For example: A. js relies on four files: 1.js, 2.js, 3.js, 4.js, which satisfy the first two criteria for code splitting (shared and larger than 30KB). In this case, five chunks are divided: A.chunk. js, 1.chunk.js, 2.chunk.js, 3.chunk.js, and 4.chunk.js. In this case, the number of concurrent requests for loading chunk~ A is exactly 5.

If A.js relies on 5.js again, 5.chunk will not be split, because if a.chunk is split, there will be 6 requests for loading A.hunk, and the contents of 5.js will be merged into A.hunk.

  • The number of requests to load code blocks at page initialization should be <=3

    The logic here and the type above are just for entry.

In addition, the default configuration is provided under splitChunks under Optimization (see here for detailed configuration instructions) :

// webpack.config.js
module.exports = {
  / /...
  optimization: {
    splitChunks: {
      chunks: 'async'.// This indicates which blocks will be selected for optimization. "Initial" | "all" (recommended) | "async" (the default) | function
      minSize: 30000.// The minimum size of the generated block in bytes
      maxSize: 0.minChunks: 1.// The minimum number of blocks that must be shared before splitting
      maxAsyncRequests: 5.// Maximum number of parallel requests when loading on demand
      maxInitialRequests: 3.// The maximum number of parallel requests for the entry page
      automaticNameDelimiter: '~'.By default, Webpack will generate names using the source and name of the block (vendors~main.js, for example). This option allows you to specify the delimiter used to generate the name.
      name: true.// The name of the split block. boolean: true | function (module, chunks, cacheGroupKey) | string
      cacheGroups: { // This is where the chunks cache group is set. SplitChunks are split according to the cacheGroups
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2.priority: -20.reuseExistingChunk: true}}}}};Copy the code

Dynamic import

Webpack provides two similar techniques when it comes to dynamic code splitting. The first, and recommended option, is to implement dynamic imports using the import() syntax that conforms to the ECMAScript proposal. The second, which is a webPack legacy, uses webPack-specific require.ensure.

Here we use the first scheme for dynamic introduction. Its introduction method is as follows:

import(/** webpackChunkName: "lodash" **/ 'lodash').then(_= > {
 // doSomething
})
Copy the code

Next, we need to modify the WebPack configuration by adding chunkFilename:

// webpack.config.js
module.exports = {
    output: {
      filename: '[name].bundle.js',
+     chunkFilename: '[name].bundle.js'.// [name] is the value of webpackChunkName when you import
      path: path.resolve(__dirname, 'dist')}};Copy the code

Other optimization

tree-shaking

If you use ES6’s import syntax, unused code is automatically removed in a production environment.

// utils.js
const add = (a, b) = > {
    return a + b;
}

const minus = (a, b) = > {
    return a - b;
}

export {
    add,
    minus
}

//index.js
import {add, minus} from './utils';
add(1.2);
Copy the code

In a project, minus is not built into bundles.

Scope Hosting scope improved

JavaScrct has function promotion and variable promotion, which promote function and variable declarations to the top of the current scope. Scope Hosting in Webpack is similar, upgrading modules.

Scope Hosting will analyze the dependencies between modules and merge the separated modules into a function as much as possible, thus reducing the cost of introducing modules. But the premise is not to create code redundancy, so only those modules that have been referenced once can be merged.

// webpack.config.js
module.exports = {
    plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
  };
Copy the code

As Scope needs to analyze the dependencies between modules, the source code must adopt ES6 modular statement, or it will not take effect.

Dynamic Polyfill service

Babel is only responsible for syntax conversion, such as converting ES6 syntax to ES5. However, if there are objects or methods that the browser does not support, for example:

  • Global objects: Promise, WeakMap, etc.
  • Global static functions: array. from, Object.assign, etc.
  • Example methods: array.prototype.includes, etc.

At this point, babel-Polyfill is introduced to simulate the implementation of these objects and methods, commonly known as shippers.

Babel-polyfill imports the entire polyfill at one time, resulting in a large file, so we use the dynamic Polyfill service for optimization.

Each time a page is opened, the browser sends a request to the Polyfill Service, which recognizes the User Agent and delivers a different Polyfill to load the effect of the Polyfill on demand.

reference

  • A Brief Introduction to Webpack Performance Optimization
  • Code Splitting Webpack Details
  • Configuration SplitChunksPlugin
  • Lin Dull webpack road – optimization chapter
  • 9 Webpack Optimization Strategies you may not know