Webpack uses three optimization environment configurations

webpack performance optimization

  • Development environment performance optimization
  • Production environment performance optimization

Development environment performance optimization

  • Optimize package build speed
    • HMR Thermal Module Replacement
      • When building, only one module changes, then only this one module will be rebuilt,
        while other modules will be read from the cache and will not be rebuilt
      • For css/js/html files, there is generally only one html file, and this operation is not required; for css files, style-loader can perform this operation by default, js does not support it by default, and you need to configure it
        yourself
  • Optimize code debugging
    • source-map technology that provides source code to built code mapping
      • To clean up development and production environments it is recommended to use
      • Recommended development environment: eval-source-map / eval-cheap-module-source-map
      • Production environment recommendation: source-map / cheap-module-source-map

Production environment optimization

  • Optimize package build speed

    • oneOf
      • When the loader corresponding to the processing file is found, stop querying, instead of going through all the loaders
      • Note: When a file needs to be processed by two or more loaders, put one of them in oneOf and the rest outside
    • babel cache
      • When building for the first time, the results of the babel build will be cached, and when building again, it will be read directly from the cache
      • Optimize the processing of js files through babel-loader
  • Optimize code performance

    • Cache (when performing mandatory caching, if the file changes, the operation performed to identify whether the change has occurred)
      • hash No matter whether the content of the file changes, when rebuilding, the hash value will be reset, resulting in rebuilding the file that has not changed
      • chunkhash comes from the same chunk (files from the same entry share the same hash value during packaging), and the hash value of the style file and js file is the same. Once the style file changes
        and the js file remains unchanged, the hash value of the js file also changes. To change; similarly, only the js file is changed without changing the style file, and the hash value of the style file also changes
      • contenthash generates a hash value based on the content of the file. The content of the file is different, and the hash value must be different
    • tree shaking
      • Remove the code that we don't use in the application (meet two prerequisites, automatically turn on tree shaking)
    • code split splits a large js file into multiple js files during construction, and then loads them in parallel, saving time
      • single entry file
      • multi-entry file

HMR : hot module replacement hot module replacement/module hot replacement

  • Function: If a module changes, only this module will be repackaged (instead of all repackaged), improving the build speed
  • Style file: HMR function can be used: because style-loader internally implements
  • js file: the HMR function cannot be used by default
  • Solution: Modify the js code and add code that supports the HMR function
  • Once module.hot is true, it means that the HMR function is enabled —> let the HMR function take effect
if(module.hot){
    
    
    // 此方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建,会执行后面的回到函数
    module.hot.accept('./print.js',function () {
    
    
        print();
    })
}
  • Note: The processing of js by the HMR function can only process non-entry js files
  • html file: HMR function cannot be used by default, and it will cause problems: html file cannot be hot updated (no need to do HMR function)
  • Solution: Modify the entry entry and import the html file
 entry: ['./src/js/index.js','./src/index.html'],

source-map:

A technology that provides a mapping relationship between source code and built code (if the built code goes wrong, the source code error can be traced through the mapping relationship)

  • [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
 devtool:'eval-source-map',

inline-source-map inline

  • Only one source-map is generated
  • Exact information about the error code and the location of the error in the source code

hidden-source-map external

  • Error code and error reason, but no error location
  • It cannot prompt the source code error, but can only prompt the error location of the code after construction

eval-source-map inline

  • Each file generates a corresponding source-map, which is generated inside built.js

nosourcrs-source-map external

  • Accurate error code information but no source code error information

cheap-source-map

  • Exact information about the error code and the location of the error in the source code
  • Can only be accurate to the row, but not to the column

cheap-module-source-map

  • Exact information about the error code and the location of the error in the source code
  • The module will add the source map of the loader

The difference between inline and external:

    1. Files are generated externally, but not inline. 2. The inline build speed is faster
  • Development environment: fast speed, more friendly debugging
  • Speed ​​(eval > inline > cheap...)
  • eval-cheap-source-map
  • eval-source-map‘
  • Debugging is more friendly
  • source-map
  • cheap-module-source-map
  • cheap-source-map
  • —> eval-source-map / eval-cheap-module-source-map
  • Production environment: Is the source code hidden? Should debugging be more friendly?
  • Inlining will increase the size of the code, so it is not necessary to inline in the production environment
  • nosources-source-map hide all
  • hidden-source-map only hides the source code, and will prompt for code errors after construction
  • —> source-map / cheap-module-source-map

oneOf

Normally, a file will be filtered and processed by all loaders. If I have 100 loader configurations, then one file will be matched by 100 loaders. After using oneOf, if the loader rule placed in oneOf has If one is matched, other rules in oneOf will not match this file again

Note: There cannot be two loader rule configurations in oneOf to process the same file, otherwise only one will take effect. For example: babel conversion after eslint detection for js

​ Solution: extract eslint to the outside, and then execute it first, so that after the external detection, the internal configuration of oneOf will be detected and matched. The
following loader will only match one
. Note: two configurations cannot process the same file

oneOf:[
                    {
    
    
                        test:/\.css$/,
                        use:[
                            // 此loader会将css代码以标签的形式整合进js代码中
                            // 'style-loader',
                            ...commonCssLoader
                        ]
                    },
                    {
    
    
                        test:/\.less$/,
                        use:[
                            ...commonCssLoader,
                            'less-loader'
                        ]
                    },
                    /**
                     * 正常来讲,一个文件只能被一个loader处理。
                     * 当一个文件要被多个loader处理时,那么一定要指定loader的先后顺序
                     * 先执行 eslint,再执行babel
                     */
                    // 在package.json中的eslintConfig ---> airbnb规则
                    // 对js做兼容性处理
                    {
    
    
                        test:/\.js$/,
                        exclude:/node_module/,
                        loader:'babel-loader',
                        options:{
    
    
                            presets:[
                                // 转译新的es6语法
                                '@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,
                        }
                    },
                    {
    
    
                        tset:/\.html$/,
                        loader:'html-loader'
                    },
                    {
    
    
                        exclude:/\.(js|html|less|jpg|png|gif|css)/,
                        loader: 'file-loader',
                        options: {
    
    
                            outputPath: 'media',
                        }
                    }
                ]

cache

babel-cache

  cacheDirectory: true
    让第二次打包构建速度更快

file resource cache

  hash: 每次wepack构建时会生成一个唯一的hash值。
    问题: 因为js和css同时使用一个hash值。
      (可能我却只改动一个文件)如果重新打包,会导致所有缓存失效。

chunkhash: code block cache

   根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
    问题: js和css的hash值还是一样的
      因为css是在js中被引入的,所以同属于一个chunk
      chunk:从一个入口文件引入的其他依赖和其他文件,都属于同一个chunk文件

contenthash

Generate a hash value based on the content of the file. The hash value of different files must be different,
so that it is easier to use the cache when the code goes online

treeShaking

It means that when I import a module, I don't import all the code of this module, I only import the code I need, which requires the help of the Tree Shaking function that comes with webpack to help us achieve it.

There is an official standard statement: the essence of Tree-shaking is to eliminate useless js code. Useless code elimination widely exists in traditional programming language compilers. The compiler can judge that some codes do not affect the output at all, and then eliminate these codes. This is called DCE (dead code elimination)

In the webpack project, there is an entry file, which is equivalent to the trunk of a tree, and the entry file has many dependent modules, which are equivalent to branches. In reality, although a certain module is relied on, only some of its functions are actually used. Through Tree-Shaking, shake off unused modules, so as to achieve the purpose of deleting useless codes.

No need to configure in webpack.config.js in production mode

Prerequisites: 1. ES6 modularity must be used 2. Open the production environment.
If these two prerequisites are met, tree shaking will be automatically enabled
. Function: reduce code size

 在package.json中配置
 "sideEffects": false 即所有代码都没有副作用(都可以进行tree shaking)
 问题: 可能会将css/ @babel/ployfill (副作用)文件干掉
 "sideEffects":["*.css"] 将css资源标记为不会进行tree shaking的资源

code splitting

Divide a packaged output chunk into multiple chunks, load in parallel, etc., speed up loading, and realize on-demand loading, etc.
Mainly split the js code

Method 1 Code splitting according to the entry file

  // 单入口 一般对应单页面应用
  // entry: './src/js/index.js',
  entry: {
    
    
    // 多入口:有一个入口,最终输出就有一个bundle
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    
    
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },

Method 2 optimization configuration item for optimization

  1. For single-entry files, the code in node_modules can be packaged into a chunk separately, and the final output
  2. For multi-entry files, it will automatically analyze whether there are public files in the multi-entry chunk. If there are, the same public files will be packaged into a separate chunk
optimization:{
    
    
    splitChunks:{
    
    
      chunks:'all'
    }
  },

Method 3 Let a file be packaged into a chunk separately through js code

// ES10 的 import语法
//通过注释,可以让js生成的打包文件带上这个名字
import(/* webpackChunkName: 'test' */'./test')
  .then(({
     
      mul, count }) => {
    
    
    // 文件加载成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    
    
    // eslint-disable-next-line
    console.log('文件加载失败~');
  });
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

Lazy loading and preloading

Application scenario: When we have a lot of modules, there are too many imported js, or some js are only useful when they are used, and I load them at the beginning, which may cause some unnecessary performance waste

1. Lazy loading: load when the file needs to be used

​ Possible problem: When the user uses it for the first time, if the js file is too large, it may cause the loading time to be too long (with delay), but it will not happen the second time, because the second time of lazy loading is to read from the cache fetch files

2. Preload prefetch: wait for other resources to be loaded and the browser is idle, then secretly load

​ Normal loading can be considered as parallel loading (loading multiple files at the same time, but there is an upper limit at the same time)

​ For example, in the following example, there is a preloaded code running effect. After the page is refreshed, but before it is used, the file has actually been loaded.

Note: Although the performance of preloading is very good, it requires a higher browser version and poor compatibility. Use preloading with caution

console.log('index.js文件被加载了~');
// import { mul } from './test';
//懒加载
document.getElementById('btn').onclick = function() {
    
    
    //懒加载其实也是需要前面Ⅵ代码分割功能,将我的需要加载的文件打包成单独文件
  import(/* webpackChunkName: 'test'*/'./test').then(({
     
      mul }) => {
    
    
    console.log(mul(4, 5));
  });
};
//预加载
//在注释参数上添加 webpackPrefetch: true 
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({
     
      mul }) => {
    
    
    console.log(mul(4, 5));
  });

PWA (offline access)

PWA: Progressive Web Development Application (offline accessible) workbox -->Download dependencies: workbox-webpack-plugin

1. Use this plug-in in configuration: ① Help serviceworker start quickly ② Delete old serviceworker

2. Add code in the entry file js

3. eslint does not know window and navigator global variables

Solution: Need to modify the eslintConfig configuration in package.json

4. The code must run on the server to be effective

① node.js

② npm i serve -g -->serve -s build starts the server and exposes all resources in the build directory as static resources

webpack.config.js new configuration

 const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
plugins: [
    new WorkboxWebpackPlugin.GenerateSW({
    
    
      /*生成一个 serviceworker 配置文件~*/
      //1. 帮助serviceworker快速启动
      clientsClaim: true,
      //2. 删除旧的 serviceworker
      skipWaiting: true
    })
  ],

Entry file js -->index.js

// 注册serviceWorker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
    
    
  window.addEventListener('load', () => {
    
    
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
    
    
        console.log('sw注册成功了~');
      })
      .catch(() => {
    
    
        console.log('sw注册失败了~');
      });
  });
}

package.json new configuration

 "eslintConfig": {
    
    
    "extends": "airbnb-base",
    "env": {
    
    
      "browser": true //开启为eslint支持浏览器端的bian'l,比如 window 
    }
  },

Multi-threaded packaging

1. Download thread-loader dependencies

2. Use loader: 'thread-loader' to enable multi-threaded packaging

Note: the process startup time is about 600ms, and the process communication also has overhead. Only when the work takes a long time, multi-process packaging is required. For example: babel conversion can use multi-threading

const {
    
     resolve } = require('path');
module.exports = {
    
    
  module: {
    
    
    rules: [
        oneOf: [
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /* 
                开启多进程打包。 
                进程启动大概为600ms,进程通信也有开销。
                只有工作消耗时间比较长,才需要多进程打包
              */
              {
    
    
                loader: 'thread-loader',
                options: {
    
    
                  workers: 2 //设置 进程2个
                }
              },
              {
    
    
                loader: 'babel-loader',
                options: {
    
    
                  presets: [
                    [
                      '@babel/preset-env',
                      {
    
    
                        useBuiltIns: 'usage',
                        corejs: {
    
     version: 3 },
                        targets: {
    
    
                          chrome: '60',
                          firefox: '50'
                        }
                      }
                    ]
                  ],
                  // 开启babel缓存
                  // 第二次构建时,会读取之前的缓存
                  cacheDirectory: true
                }
              }
            ]
          }
        ]
      }
    ]
  }
};

externals

When you use external import code: such as CDN import, if you don’t want him to package the modules I import, you need to add this configuration

That is: Declare which libraries are not packaged
–> externals: {}

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

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    })
  ],
  mode: 'production',
  externals: {
    
    
    // 拒绝jQuery被打包进来
    jquery: 'jQuery'
  }
};

dll

Use dll technology to package some libraries (third-party libraries: jquery, react, vue...) separately

Function: If you use a third-party library instead of CDN import, and want to expose it after packaging, use this method

1. First, you need to write a new configuration file, because it uses dll technology, so it is named webpack.dll.js

When you run webpack, it looks for the webpack.config.js configuration file by default Requirements: You need to run the webpack.dll.js file first

–> webpack --config webpack.dll.js In this file, some libraries are packaged separately

2. In webpack.config.js, you need to tell webpack which libraries do not need to be packaged again (that is, the files generated after packaging in dll.js)

3. Here you need to use add-asset-html-webpack-plugin and webpack plugin

4. After running webpack.dll.js to package the third-party library separately, unless you want to add a new library, you don’t need to repackage this, just pack the others directly with webpack

webpack.dll.js configuration file

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

module.exports = {
    
    
  entry: {
    
    
    // 最终打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的库是jquery
    jquery: ['jquery'],
   //  react:['react','react-dom' ]
  },
  output: {
    
    
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  },
  plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射
    new webpack.DllPlugin({
    
    
      name: '[name]_[hash]', // 映射库的暴露的内容名称
      path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
  ],
  mode: 'production'
};

webpack.config.js configuration file

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'
    }),
    // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
    new webpack.DllReferencePlugin({
    
    
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
    
    
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],
  mode: 'production'
};

Guess you like

Origin blog.csdn.net/csdssdn/article/details/126180585