Content of this article:

  1. Reduce webPack packaging time, how to speed up WebPack packaging
  2. What are some ways to make webPack print smaller packages
  3. Load code spliting on demand (1. Extract common code 2. Component load on demand) + (dynamic load JS resources in component)

The purpose is: fast + small + on-demand loading

One is speed, one is volume, one is what is used to load what

Reduce webPack packaging time

Optimizing Loader Configuration

For Loaders, the most important factor affecting packaging efficiency is Babel. Because Babel converts code into strings to generate aN AST, and then converts the AST into new code, the larger the project, the more code it converts, and the less efficient it is. Of course, there are ways to optimize it.

First we can optimize the file search scope of Loader

The path of the files processed by Loader, specified with the path of the files not processed. And what type of files does it apply to

module.exports = {
  module: {
    rules: [{// Use Babel only for js files
        test: /\.js$/.loader: 'babel-loader'.// Look only in the SRC folder
        include: [resolve('src')].// Not to find the path
        exclude: /node_modules/}}}]Copy the code

For Babel, we definitely want it to work only on JS code, and then the code used in node_modules is all compiled, so there’s no need to go through it again.

Of course, this is not enough. We can also cache the Babel compiled files and only compile the changed code files next time, which greatly speeds up the packaging time

loader: 'babel-loader? cacheDirectory=true'Copy the code

Optimize Load configuration summary:

  • For example, the path babel-Loader looks for is specified with the path it does not look for
  • Cache Babel compiled files


HappyPack turns on multithreaded packaging

Since Node is single-threaded, Webpack is also single-threaded during the packaging process, especially when Loader is executing, and there are many long compilation tasks, which can lead to waiting situations.

HappyPack converts the synchronized execution of the Loader to parallel, thus making full use of system resources to speed up packaging efficiency

module: {
  loaders: [
    {
      test: /\.js$/,
      include: [resolve('src')], exclude: /node_modules/, // id'happypack/loader? id=happybabel'
    }
  ]
},
plugins: [
  new HappyPack({
    id: 'happybabel',
    loaders: ['babel-loader? cacheDirectory'], // Open 4 threads: 4})]Copy the code

HappyPack summary:

  • Use this plug-in to start multithreaded packaging and make full use of system resources


DllPlugin (prepackage the specified class library)

DllPlugin can pre-package and introduce specific class libraries. This approach greatly reduces the number of times the library needs to be repackaged, only when the library is updated, and it also optimizes the separation of common code into separate files.

Now let’s learn how to use DllPlugin

// In a separate file
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    // Libraries that want to be packaged uniformly
    vendor: ['react']},output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].dll.js'.library: '[name]-[hash]'
  },
  plugins: [
    new webpack.DllPlugin({
      // Name must be the same as output.library
      name: '[name]-[hash]'.// This property needs to be consistent with the DllReferencePlugin
      context: __dirname,
      path: path.join(__dirname, 'dist'.'[name]-manifest.json')]}})Copy the code

We then need to execute the configuration file to generate the dependency files, which we then need to import into the project using the DllReferencePlugin

// webpack.conf.js
module.exports = {
  / /... Omit other configurations
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      // Manifest is the json file that was packaged before
      manifest: require('./dist/vendor-manifest.json'),}})]Copy the code

DllPlugin summary:

  • Pre-pack and introduce custom libraries to reduce the number of times of packing libraries and speed up packaging
  • At the same time, the common code is extracted into a separate file


Parallel code compression

In Webpack3, we normally use UglifyJS to compress the code, but this is single-threaded. To make it more efficient, we can use the Webpack-parallle-Uglify-plugin to run UglifyJS in parallel.

In Webpack4, we don’t need to do any of these things. We just need to set mode to Production to enable this function by default. Code compression is also a must for performance optimization. Of course, we can compress not only JS code, but also HTML and CSS code. In the process of compressing JS code, we can also implement configuration functions such as deleting console.log code.

Code parallel compression summary

  • Webpack4 production mode. By default, multiple child processes (threads) are enabled. Instead of one by one compression, parallel compression is changed


Some small optimization points

There are also some small optimizations we can make to speed up packing

  • resolve.extensions: indicates a list of file suffixes. The default lookup order is['.js', '.json']If your import files do not have suffixes, you will find files in this order. We should try toReduce the length of the list of suffixes, and then list the most frequent suffixes first
  • resolve.alias: Can passAlias to map a pathAllows Webpack to find the path faster
  • module.noParseIf you are sure that there are no other dependencies under a file, you can use this property to make WebpackDo not scan the file, which is useful for large libraries



Reduce the size of Webpack files

Scope Hoisting

Scope colliers will analyze the dependencies between modules and try to merge the packaged modules into a function as much as possible.

Because Scope Hositing needs to analyze dependencies between modules, the source code must use ES6 modular statements or it won’t work, just like Tree Shaking

Let’s say we want to package two files

// test.js
export const a = 1
// index.js
import { a } from './test.js'
Copy the code

In this case, our packaged code would look something like this

[
  /* 0 */
  function (module, exports, require) {
    / /...
  },
  / * 1 * /
  function (module, exports, require) {
    / /...}]Copy the code

However, if we use Scope colliers, the code will be merged into one function as much as possible, so it will become similar code like this

[
  /* 0 */
  function (module, exports, require) {
    / /...}]Copy the code

This packaging approach generates significantly less code than the previous one. If you want to open this function in the Webpack4, only need to enable optimization. ConcatenateModules is ok.

module.exports = {
  optimization: {
    concatenateModules: true}}Copy the code

Scope collieries summary:

  • Whenever possible, merge the packaged modules into a single function


Tree Shaking (Default for WebPack 4 production)

Tree Shaking is a term often used to remove dead-code from JS when packaging. It relies on the static structure features of imports and exports in the ES6 module system

Tree Shaking can remove unreferenced code from a project, for example

// test.js
export const a = 1
export const b = 2
// index.js
import { a } from './test.js'
Copy the code

In this case, the variable b in the test file will not be packaged into the file if it is not used in the project.

If you are using Webpack 4, this optimization feature is automatically enabled when you start the production environment.


Iii. On-demand loading + Dynamic loading + Asynchronous Loading (to be improved)

According to the need to load

Presumably everyone in the development of SPA projects, there will be a dozen or more routing pages in the project. If we package all of these pages into a single JS file, it will combine multiple requests, but it will also load a lot of unnecessary code and take longer. So in order to present the home page to the user faster, we certainly want the home page to load as small as possible, at this time we can use on-demand loading, each routing page packaged as a separate file. Of course, not only can routes be loaded on demand, but also for large libraries such as LoadAsh.

The implementation of the load on demand code will not be expanded here because the implementation is different depending on the framework used. Of course, their usage may be different, but the underlying mechanism is the same. When it is used, it downloads the corresponding file, returns a Promise, and executes a callback when the Promise is successful.

Load on Demand summary:

  • Third-party component libraries are loaded on demand
  • Routing pages are packaged as separate files that can be loaded on demand to speed up home page access. When other pages are accessed, the corresponding JS is loaded

Load Code Splitting on demand

When we first started using Webpack, we packaged all the JS files into one
build.jsIn the file (filename depends on in
webpack.config.jsIn the file
output.filename), but in larger projects,
build.jsMay be too large, resulting in a long page load time. That’s when you need it
code splitting.
code splittingThat’s splitting the file into chunks
(chunk)We can define some split points
(split point)According to these split points, the file is divided into blocks and the on-demand loading is realized.


We can configure the router to load components on demand, which can reduce the size of build.js and optimize the load speed when the individual component files are large. (If the component is small, it will increase the load time by adding additional HTTP requests.)

There are two ways to load on demand:

  • Third party libraries are packaged separately: Since the contents of the third party libraries rarely change, they can be separated from the business code to maximize the use of the browser’s caching mechanism and reduce the number of requests.
  • Load on demand:

    Webpack supports defining split points throughrequire.ensureLoad on demand.

  • (1) Third-party class libraries are individually packaged

    Assuming that the project introduces jjquery. Js and respond.js, we can configure multiple entries in webpack.config.js to package the two third-party libraries separately.

    • Do the configuration in webpack.config.js

      //webpack.config.js // Add the corresponding third-party class library to the entry: {bundle:'./src/main.js',
          vendor: ['the. / SRC/lib/jquery - 1.10.2. Min. Js'.'./src/lib/respond.min.js']} / / in the plugins add CommonChunkPlugin plugins: [new webpack.optimize.Com monsChunkPlugin ({name:'vendor',  
              filename: 'vendor.bundle.js'})]Copy the code
    • Run NPM run build. At this time, two files are generated in the dist directory, namely build.js and vendor.bundle.js






    (2) AMD CMD component load configuration on demand

    //app.js
    
    import Vue from 'vue'
    import App from './App.vue'
    import VueRouter from 'vue-router'Use (VueRouter) // const ComA = resolve => require(['./components/A.vue' ], resolve);
    // const ComB = resolve => require(['./components/B.vue' ], resolve);
    // const ComC = resolve => require(['./components/C.vue'], resolve); Const ComA = resolve => require. Ensure ([], () => resolve(require())'./components/A.vue')));
    const ComB = resolve => require.ensure([], () => resolve(require('./components/B.vue')));
    const ComC = resolve => require.ensure([], () => resolve(require('./components/C.vue')));
    
    const router = new VueRouter({
      routes: [
        {
          name: 'component-A',
          path: '/a',
          component: ComA
        },
        {
          name: 'component-B',
          path: '/b',
          component: ComB
        },
        {
          name: 'component-C',
          path: '/c',
          component: ComC
        }
      ]
    })
    
    new Vue({
      el: '#app',
      router: router,
      render: h => h(App)
    })Copy the code

    • Configure output.chunkfilename in webpack.config.js,

    //webpack.config.js
    
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js'// add chundkFilename to chunkFilename:'[name].[chunkhash:5].chunk.js'
    }Copy the code


    perform
    npm run buildAt this time,
    distFive files are generated in the directory, and the extra three files are the corresponding
    A.vue.
    B.vue.
    C.vueThese three components



    summary

    In this chapter, we learned how to use Webpack to optimize performance and reduce packaging time.

    Versions of Webpack change quickly, and optimizations can vary from one version to the next, so I didn’t use too much code to show how to implement a feature. The point of this chapter is to learn how we can optimize, and specific code implementations can look for specific versions of the code.

    This article is from the Gold Mining booklet


    Summary (fast + Small + On-demand loading) :

    • Fast – Packaging speed (Loader configuration optimization, specifying which path to process, which not to process, parallel compression, parallel compression, third-party dependencies to do caching do not repeat)
    • Tree -shaking removes useless code + modules into a function + pages packaged as separate Js, speeding up the home page
    • Load components and third-party resources on demand