One, foreword

  • I believe that every front end of Webpack is familiar, I have been in the industry for several years, I still have a very shallow understanding of Webpack, based on the convenience of using various scaffolding, what happens behind each run NPM run dev/build, what things do, I have never considered, I think it is time to light up this skill tree.
  • In the process of learning Webpack, I also encountered many difficulties. It was difficult to find the latest systematic learning materials, most of which were a little old. When we download the dependency package in NPM, we are used to download the latest version, and many configurations are different from the version in learning materials, or some configuration plug-ins are deprecated at this time, this time you have to find a replacement plug-in. In consideration of the frequent version problems encountered in learning WebPack, I will carry the version number for each of the following installation packages.
  • Write a blog for the first time, do not like spray, have a question welcome to point out.
  • Learning approach:
    • Webpack In action: Getting Started, Advancing, and Tuning
    • Video moOCs “From the basics to the actual combat to take you to master the new WebPack4”
    • The official documentation

2. Get to know Webpack

  1. What is a webpack
    • Webpack is a javascript packaging tool. Its core function is to solve the dependency between modules, organize multiple templates together according to rules, and finally merge them into a JS file. This process is called module packaging.
  2. Why webpack
    • In the early stage, only one editor is needed to develop a Web application. With the diversification and complexity of Web applications in recent years, the maintenance cost of the code becomes difficult to manage has increased significantly. The birth of Webpack modularization makes development efficiency significantly improved
    • Problems solved by modularization:
      • You can clearly see the dependencies between modules through import and export.
      • Only the merged resources need to be loaded on the page to reduce the network overhead.
      • Multiple modules are scoped separately and do not have naming conflicts with each other.

3. Webpack core

3.1 installation

// Initialize a directory first
npm init -y
// Version: [email protected] [email protected]
npm i webpack webpack-cli -D
Copy the code
  • Webpack installation is divided into global installation and project local installation
    • The advantage of a global installation is that one installation runs everywhere. But when working with a multiplayer project, each person has a different version of Webpack, which can lead to inconsistent input results. Plug-ins that partially rely on WebPack can be confusing if they exist both globally and locally.
    • Project local installation, which can only be used within the project, but ensures that everyone has the same version number. (recommended)

3.2 run

  • It can run directly in the global environmentwebpackCommand, which needs to be run under the projectnpx webpackThis is because it runs in a global environmentwebpackThe command will look for webpack globally, and NPX will look for Webpack in node_modules in the project directory
  • Package. The json configuration
 /* Run the webpack command in the package.json file. If the webpack is not found, run the webpack command globally. * /
  "scripts": {"build":"webpack"
  }
Copy the code

3.3 entry

  • At the very beginning of the Webpack process, you need to specify one or more entry files that tell WebPack exactly which file in the project directory to start packing from
  • Consider the dependencies of each module in the project as a tree, and the entry is the root of the tree. Webpack retrieves from the entry file, generates a dependency tree of the dependency modules, and ends up with a bundle of artifacts packaged by chunk. The diagram is as follows

  • Entey configurations can be strings, arrays, objects, and functions
module.exports = { 
    entry:'./src/index.js'./ / a single entrance
    // Array: Premerges multiple resources. When packaging, the last element is the actual path, and other elements are introduced in the last element, resulting in a bundle file.
    entry: ['lodash'.'./src/index.js']./* The above array behavior is equivalent to the introduction of lodash*/ in SRC /index.js 
    // Object: multiple entries, key as file name, val as file entry path, several attributes to produce several bundles.
    entry: {index:'./src/index.js'.app:'./src/app.js',},// function: returns any of the above types. The purpose of defining function is to dynamically configure the entry path.
    entry:() = >({index:'./src/index.js'.app:'./src/app.js'})}Copy the code

3.4 Resource Output

  • Output means where does WebPack output the file it creates, and how does it get named
module.exports = { 
    entry:'./src/index.js'.output: {filename:'[name].js'.// File name. The default template variable is main. If there are multiple entries, the file name is entry key
        library: {// Expose a global variable of some type
            name:"someLibName".// Specify the library name as someLibName
            type:'global'.// This,window, commonJS, umD, etc.
        }, 
        // The directory where the resource is generated, indicating that the file filename is packed into the dist directory.
        path:path.resolve(__dirname,'dist'),
       
        chunkFilename:'[id]@chunk.js'.// Specify the file name of the non-entry file, that is, other files imported by the entry file
        CleanWebpackPlugin is the same as CleanWebpackPlugin, but clean only removes the changed files. CleanWebpackPlugin removes all files
        clean:true./* The indirect resource path requested by JAVASCRIPT and CSS, the page resources are divided into two kinds, one is loaded through the HTML tag script, and the other is loaded through JS, CSS asynchronously. PublicPath specifies where these indirect resources are requested. * /
        publicPath:' './* About publicPath: PublicPath relative path is specified for an HTML, with the current HTML page path when they request resources and relative paths, constitute the actual path Assume that the address is https://www.baidu.com/app/index.html asynchronous loading the file name: main.js publicPath:"", / / the actual path for https://www.baidu.com/app/main.js publicPath: ". / js "publicPath: / / https://www.baidu.com/app/js/main.js".. CDN related: / / https://www.baidu.com/js/main.js/js "when publicPath in the form of agreement or relative agreement at the beginning, said path is CDN. Assuming that the CDN address is: http://code.jquery.com/jquery-migrate-1.2.1.min.js loading resource name: jq-cdn.js publicPath:'https://cdn.com' // https://cdn.com/jq-cdn.js publicPath:'//cdn.com/assets' // /cdn.com/assets/jq-cdn.js */}}Copy the code

3.5 loader

  • A Web project will contain HTML, JS, CSS, images, fonts, and frames (.vue), while Webpack only knows JS files, loader gives WebPack the ability to handle different resource types.
/ / webpack. Config. Js configuration
mudole.exports = { 
    // Other configurations.....
    module: {rules: [{// The versions of the CSS installation packages are [email protected],[email protected],[email protected],[email protected], and [email protected]
            test:/\.css$/, 
            use:[ 
                'style-loader'.'css-loader'.// Browser compatibility with CSS, such as adding vendor prefixes to create a postcss.config.js file for configuration
                'postcss-loader'.// The related configuration can be configured by creating a separate postcss.config.js
                Postcss-loader :'postcss-loader', options:{postcssOptines:{plugins: [ 'postcss-preset-env' ] } } } */],},/* There are four new modules in Webpack5: raw-loader,url-loader, and file-loader. The urL-loader asset/ Resource file output is packaged into the directory and the file-loader asset/inline is injected as the base64 encoding. Corresponding to url-loader asset/source export resource source code, corresponding to raw-loader */ 
        { 
        // The installation package required by Babel: @babel/[email protected] @babel/[email protected] [email protected] [email protected]
            test:/.\js$/, 
            exclude:/node_modules/.// Non-mismatch, node_modules is not packaged.
            use:['babel-loader'] // Create a.babelrc file in which the babel-related configuration is configured}, {// You don't need to do much with the font icon file, just go to the package directory as it is.
            test:/\.(woff|ttf|svg|eot|woff2)$/, 
            type:'asset/resource'.// Output file resources to the package directory, previously implemented by file-loader,
            generator: {filename:'fonts/[name][ext][query]'// Resource file name}, {},Processing of images is / * ever, is greater than the specified byte size, image resources package to the directory, or in the form of base64 injection, through the following configuration parser. DataUrlCondition. MaxSize * / implement the effect
            test:/\.(png|jpg|gif")$/, 
            type:'asset'.generator: {filename:'imgs/[name][query]' 
            }, 
            parser: {dataUrlCondition: {/* > 4KB, type is treated as resource(the file is output to the package directory), otherwise inline(injected into the package as a Base64 encoded string). * /
                    maxSize:4*1024}}}]}}// postcss.config.js Postcss has many configurations. Create a separate file for configuration
module.exports = { 
    plugins: [ 'postcss-preset-env']}// Add browser compatibility configuration to package.json
"browserslist": [ 
    "1%" >."last 2 versions"."not ie <= 8" 
] 
// Create a.babelrc file, which can be thought of as a JSON file.
{ 
    "presets":[ 
        [ 
            "@babel/preset-env",
            { 
                "targets": {// Browser compatible configuration, compatible with lower versions of the browser, install a core-js package, otherwise an error will be reported.
                    "chrome":"67",},"corejs":"3"./ / corejs version
                "useBuiltIns":"usage"// Load as needed}}]]Copy the code

3.6 the plugin

  • Plugin is designed to extend webPack functionality by injecting hook implementations into the build process, giving WebPack a lot of flexibility. Can resolve loader cannot implement things.
  • Here are some simple and commonly used plugins to understand how to use them, and more plugins will be introduced later
// Installation package version: [email protected] [email protected]
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  /// Other configurations....
  plugins: [// instantiate the plug-in
      // HtmlWebpackPlugin generates an HTML file for us by packing it into the package directory (dist) and dynamically importing the bundle output. By default, HtmlWebpackPlugin generates an HTML file for us. Tamplate is configured to reference the local HTML file and retain the HTML element attributes.
      new HtmlWebpackPlugin({
          template:"./index.html".// An HTML file in the root directory,
      }),
      // CleanWebpackPlugin cleans up all files in the package directory (dist) before each package. Output.clean =true is also available in Webpackage 5, which is easier to use and more intelligent (only delete changed resource files).
      new CleanWebpackPlugin()
  ]
}
Copy the code

3.7 mode

module.exports = { 
    // Other configurations....
    // Mode has two packaging modes, development: production
    mode:'development'
}
Copy the code

3.8 the Source Map

  1. Source Map refers to the process of mapping compiled, packaged, and compressed code back to Source code, and tracing errors back to Source code as they occur. The more detailed the error information is, the more performance is affected, such as tracing the error to the exact line and byte, which is very expensive and unnecessary.
  2. The output configuration for Source Map is devtool. With devtool enabled, the Source Map will be passed along with the Source code step by step. The default file name is the packaged file name plus.map, such as main.js.map

  1. The map file is loaded only when the browser developer tool (console) is opened. The map file is not loaded unless the browser developer tool is opened. The browser uses the loaded map file to parse the packaged bundle to find out the structure and contents of the file directory.
  2. The Source Map configuration
    • These configurations can be free combination, before webpack5 webpack5 after need to follow a sequential specification: – [the inline – | hidden – | eval -] [nosources -] [being – [module -]] source – the map
devtool instructions
empty The Source Map file is not generated
eval Wrap the module you want to install with an EVAL statement
source-map Generate a separate map file
hidden Without adding a reference to the Source Map file in the bundle, the browser will not automatically load the Source Map file
inline Convert the generated Source Map to Base64 format and embed it in a javascript file
cheap The generated Source Map does not contain column information, so the calculation is less, the Source Map file is smaller, and the content of the third party module will not be used
module In addition to errors in your own business code, you also manage errors in third-party templates
module.exports = {
    / /... Omit other configurations
    Developement :'eval-cheap-module-source-map' production:'cheap-module-source-map' */
    devtool:'cheap-module-source-map'
}
Copy the code

3.9 resolve

  • Resolve configures how WebPack will find the file for the module, starting from the configured entry module.
const path = require('path')
module.exports = {
    / /... Omit other configurations
   resolve: {/* Extensions contain extensions that can be omitted when importing files. Do not over-reference files such as.css.ts.png.jpg, which can cause performance problems because every time a file is introduced it will run through all the suffixes in the Extensions. * /
       extensions: ['.js'.'.json'].// The name of the file used to parse the directory, first look for index.js, then look for pagea.js
       mainFiles: ['index'.'pageA'].alias: {// Configure the path alias to simplify the path.
           util:path.resolve(__dirname,'src/util/'),},/* Configure the directory where webpack looks for third-party directories. By default, it only looks for third-party directories in node_modules, so you don't need to configure it unless you have special requirements, such as shortening the path of the components library in your project. Just import Button from 'Button' */
       modules: ["./src/components"."node_modules"].}}Copy the code

3.10 devServer

  • This configuration applies only to the development environment to improve development efficiency. When you start devServer, you run your packaged files on a simple Web server provided by Webpack-dev-server, which temporarily stores the packaged files in memory and automatically refreshes the page for you when the content changes.
// Installation package version: [email protected]
/ / webpack. Config. Js file
module.exports = {
    / /... Omit other configurations
    devServer: {//hot:true enables module hot replacement.
        open:true.// Open the development page of your default browser when devServer starts and builds for the first time
        port:3002./ / the port number
        compress:true.// Whether to enable gzip compression.
        host:'0.0.0.0'.// Specify the host to use so that the server can be accessed externally.
        // The configuration item can inject some HTTP response headers into the HTTP response and add headers to all the responses
        headers: {'X-Custom-Foo':'bar'
        },
        // Configure the whitelist
        allowedHosts: ['host.com'.'subdomain2.host.com'.'host2.com'].// Solve the single-page routing problem, which requires the server to return an HTML file for any hit route
        historyApiFallback: {rewrites:[
                {form:/abc.html/,to:'/index.html'}},proxy: {'/api':'http://localhost:3000'.// If there is a '/ API 'in the path, the proxy is used
            /********** can also be written as '/ API ':{target:'http://localhost:3000', // By default, servers running over HTTPS with invalid certificates are not accepted, If secure is set to false Secure :false, pathRewrite:{// Return footer data 'head.json':'footer.json'}, // Sometimes do not want to proxy all requests, The agent can be bypassed based on the function return value. Bypass :function(req,res,proxyOptions){return null and undefined if the proxy is used to process the request. Do not proceed with the proxy request}} */
        },
        / * * * * * * * multiple paths to the same target agent proxy: [{context: ['/API ', '/ house], target:' http://localhost:3000 '}] * * * * * * * * /}}/ / package. Json file
{
    "scripts"{
        "dev":"webpack-dev-server"}}// Then run NPM run dev
Copy the code

Four, Webpack advanced

4.1 Module Hot Replacement (HMR)

  • Module hot replacement is the ability to display the latest changes to the page without refreshing the page or even re-initiating the request to see the updated effect.
  • Module hot replacement is manually enabled and is done under webpack-dev-server or webpack-dev-miedle. The webpack command does not support HMR
const webpack = require('webpack')
module.exports = {
    // Omit other configurations....
    devServer: {/ /... Inherit the configuration from 3.10
        hot:true.// Start the hot module
    },
    plugins: [new webpack.HotModuleReplacementPlugin()
    ]
}
Copy the code
  • By default, HRM does not save the previous data when js file content changes, which is obviously not what we want. Need to manually listen to js file changes to make processing
// number1.js
export function countAdd(){
  const div = document.createElement('div')
  let count = 1
  div.innerHTML = count
  document.body.append(div)
  div.onclick = function(){
    count++
    div.innerHTML = count
  }
}

// number2.js
export function getCount(){
  const div = document.createElement('div')
  div.id="constcount"
  div.innerHTML = 'I'm number2'
  document.body.append(div)
}
//index.js
import {countAdd} from './number1'
import {getCount} from './number2'
countAdd()
getCount()

if(module.hot){ 
  module.hot.accept('./number2'.() = >{
    getCount()
  })
}
Copy the code
  • This code is the dom generated each time you click on number1, and the innerHTML of the DOM generated by Number1 increments with a number. If you’re not listening to the number2 file, then every time the innerHTML of number2 changes, The innerHTML of number1 resets the number.
  • When we were writing CSS, the Vue code didn’t do this because its corresponding loader(style-loader,vue-loader) did it for us.
  • About the HMR principle
    • In the local development environment, the browser is the client, and the webpackDevServer is the server, and the core of HMR is the client to pull updated resources from the server.
    • When the browser pulls these updated resources depends on how the webpackDevServer listens for local resources. In fact, webpackDevServer maintains a WebSocker between the browser and the browser. When a local resource changes, webpackDevServer pushes an update event to the browser, along with the hash of this build, allowing the client to compare with the last one to prevent redundant updates, since changes in the source file do not represent changes in the build result.
    • This also explains why when multiple local pages are open at the same time, all pages are updated whenever the code changes. Webscoker just wants to know if the build is the same as the last one. When the client knows that the new build is different from the current one, it sends a request like webpackDevServer to get a list of the change files, which modules have changed.

4.2 Multi-page packaging

  • Suppose there is a requirement that there is an H5 page and a PC page in the project. Package them separately and do not affect each other. After packaging, generate H5.html and PC.html respectively and introduce corresponding bundle files to each other independently
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.export = {
    entry: {/ / entrance
        h5:'./src/h5/index.js'.pc:'./src/pc/index.js'
    },
    output: {path:path.resolve(__dirname,'dist'),
        filename:'[name].js'
    },
    plugins: [// Each HtmlWebpackPlugin creates a separate HTML file and imports the corresponding bundle.
        new HtmlWebpackPlugin({
            template:'index.html'.filename:'h5.html'.chunks: ['h5'],}).new HtmlWebpackPlugin({
            template:'index.html'.filename:'pc.html'.chunks: ['pc'],})]}Copy the code

4.3 shimming

Webpack needs to do code compatibility during the packaging process, such as @babel/ Polyfill. It solves the problem that code running on lower version browsers cannot be compatible during the packaging process, such as the lower version browsers do not know the Promise API. It implements global variables that are compatible with lower-browser promises during the packaging process, which is essentially shimming

// jquery-ui.js
export function ui1(){$('body').css('background'.'green')}// index.js
import $ from 'jquery'

import {ui1} from './jquery-ui'

function ui2(){
  const dom = $('div'The $()'body').append(dom.html('hello world'))
}
ui1()
ui2()
Copy the code
  • The above code seems to work, but in fact it will fail because ui1’s “$” is not a method. Because WebPack is based on module packaging, the variables in the module can only be used in the current module, there is no coupling between modules. Ui2’s $can be referenced because they are in the same module, whereas UI1 requires jquery alone to use “$”. In practice ui1 is likely to be a third-party package and it is impractical to modify third-party packages, but shimming is a good solution for this problem.
const webpack = require('webpack')
module.exports = {
    / /... Other configuration
    plugins: [new webpack.ProvidePlugin({// WebPack built-in plug-in
            $:'jquery'._:'lodash'._join: ['lodash'.'join']// It can also be configured like this}})]/* if the "_","_join" fields are encountered in the module, Import $form 'jquery' import _ form 'lodash' import _join form 'lodash/join' */
Copy the code

4.4 CSS Code Segmentation

  • By default, CSS code is packaged into JS, the concept of CSS in JS, rather than separate CSS. The [MiniCssExtractPlugin] plugin is needed if the imported CSS file is packaged to produce a CSS file.
// Installation package version: [email protected] [email protected]
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
module.exports = {
    / /... Other configuration
    mode:'production'.// The production environment CssMinimizerPlugin is required to be effective
    module: {rules: [{test:/\.css$/,
            use:[
                MiniCssExtractPlugin.loader,/ / replacement style - loader
                'css-loader']]}},optimization: {// After CSS is isolated, CSS code is compressed using the [CssMinimizerPlugin].
        minimizer: [new CssMinimizerPlugin()
        ]
    },
    plugins: [new MiniCssExtractPlugin({
            // The name of each CSS file produced, similar to output.filename
            filename:'css/[name].css'.// Non-entry chunk CSS file name, similar to output.chunkfilename
            chunkFilename:'css/[hash].css',}})]Copy the code

4.5 Production/development mode packaging.

  • There may be development and production environments during development, and webpack.config.js in each environment has the same and different configurations. Js (dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev/dev) Use the [webpack-merge] tool to merge and export the configuration.
// package.json
{
   "scripts": {"build":"webpack --config config/prod.config.js"."dev":"webpack-dev-server --config config/dev.config.js"}}Copy the code
  • Common configuration
// config/common.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry:'./src/index.js'.output: {filename:'[name].js'.// The current file is in the config directory, not the root directory,.. /dist output to the upper directory
        path:path.resolve(__dirname,'.. /dist')},module: {rules:[
            {
                test:/\.js$/,
                use:['babel-loader'].exclude:/none_modules/
            },
            // Loader configuration related to other resources is almost shared, such as CSS TS preprocessing image text icon, omitted here]},plugins: [new HtmlWebpackPlugin({
            template:'./index.html'}})]Copy the code
  • Production configuration
// config/prod.config.js
// Package version: [email protected]
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const {merge} = require('webpack-merge')
const common = require('./common.config')
const path = require('path')

const config = {
    mode:'production'.devtool:'cheap-module-source-map'.module: {rules:[
        {
          test: /\.css$/,
          use: [
            MiniCssExtractPlugin.loader,
            'css-loader']},]},plugins: [new MiniCssExtractPlugin({
          filename: "[name].css".chunkFilename: "[id].css"
        }),
        new CleanWebpackPlugin()
    ],
    optimization: {usedExports:true,}}module.exports = merge(common,config) // Merge the configuration and export it
Copy the code
  • The development of the configuration
// config/dev.config.js
const webpack = require('webpack')
const {merge} = require('webpack-merge')
const common = require('./common.config')

const config = {
    mode:'development'.devtool:'eval-cheap-module-source-map'.module: {rules:[
        {
          test: /\.css$/,
          use: [
            'style-loader'.'css-loader']},]},devServer: {static: {
          directory: path.join(__dirname, 'dist'),},port:3003.hot:true
    },
    plugins: [new webpack.HotModuleReplacementPlugin()
    ]
}
module.exports = merge(common,config) // Merge the configuration and export it
Copy the code

4.6 Environment Variables

  • In distinguishing the configuration between development and production environments, you may need environment variables to differentiate

Json scripts, which defaults to true if only a variable is declared and no value is assigned. For example, –env production defaults to env.production = true

{
    "scripts": {"build":"webpack --env production --config config/common.config.js"}}Copy the code

Module. exports in webpack.config.js can accept environment variables if it is a function. When you run the build command and env.production is true, it indicates production environment, and you can do some configuration related to production environment.

const {merge} = require('webpack-merge')
// Here prod and dev reference the code in the 4.5 example, internally normal module.exports throw configuration
const prod = require('./prod.config')
const dev = require('./dev.config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = (env) = >{
    const isDev = env.production
    // Decide whether to merge the configuration exported from the production file or test file based on environment variables, and then return the merged configuration.
    returnmerge(isDev? prod:dev,{mode:isDev?'production':'development'.// Depending on the environment, which packaging mode is used
        module: {rules: [{test:/\.css$/,
                use:[
                   // In the 4.5 example, we use different loaders in different environments to write two pieces of code, which is very convenient to distinguish by environment.isDev? MiniCssExtractPlugin.loader:'style-loader'.'css-loader']]}},plugins: [// Use the MiniCssExtractPlugin only in production
            isDev&&newMiniCssExtractPlugin plug-in ({filename: "[name].css".chunkFilename: "[id].css"})}}),]Copy the code

V. Performance optimization (production environment)

5.1 the Tree Shaking

  • Tree Shaking helps us detect templates that are not referenced in the project during packaging. This part of the code is never executed (dead code), and WebPack marks this part of the code and eventually removes it from the bundle when the resources are compressed
  • Prerequisites for using TreeShaking
    • TreeShaking only works on ES6Module (static)
    • Sometimes third-party libraries introduced, TreeShaking, do not work with third-party libraries. This may be because third-party libraries are still using CommonJS for better compatibility
/ / util. Js file
export function add(a,b){
  return a+b
}
// ---------- The following code is never executed
export function minus(a,b){/ / dead code
  return a-b
}
export function ride(a,b){/ / dead code
  return a*b
}

/ / SRC/index. Js file
import {add} from 'util/'
cont num = add(3.5)

// webpack.config.js
module.exports = {
    / /... Omitted configuration code
    optimization: {usedExports:true.// Treeshaking eliminates dead code}}Copy the code
  • In bundles packaged in development mode, we can see that the minus and ride method exists but is not referenced. In production mode, minus and ride is not packaged into bundles

  • When introducing global code, it is considered dead by Tree Shaking. You need to configure sideEffects in package.json, which lets Webpack remove tree Shaking code
  • Side effect: Code that performs special behavior when importing, rather than just exposing one or more exports. Polyfill, for example, affects the global scope but does not provide export
// src.index.js
// Introduce global code
import 'style.css'
import 'common.less'

/ / package. Json file
{
// Set.css,.less files as side effects
    "sideEffects": ["*.css"."*.less"]}Copy the code
  • Babel-loader will parse ES6 modules into commonJS by default. This will invalidate Tree Shaking.
/ /. Babelrc file
{
  "presets":[
    [
      "@babel/preset-env", {"modules": false // Turn off module parsing.}}]]Copy the code

5.2 Narrowing the file search scope

  • As the project grows larger and more files become available, the problem of slow build speed will also be exposed. By reducing the scope of file search, speed can be effectively increased
    • To ensure that as few files as possible are processed by loader, configure test,include, and exclude to match only the files that need to use the rules
    • Tell WebPack to ignore the parsing of jquery or LoDash to improve build performance. There are no modular statements called in these large libraries, and webPack’s parsing and processing of these statements is unnecessary and will degrade build performance. Code 2
    • As mentioned earlier (Section 3.9), having more content in the Extensions list can cause performance problems, and every time a file is found to have been introduced, all suffixes in the Extensions are traversed and used as little as possible. Code 3
    • Some third-party code may have several sets of code, such as commonJS specification code and a complete code file without any dependencies. By default, commonJS code is used. If the file depends on multiple files, WebPack will handle the corresponding dependencies. Like react, webPack will default to its entry file./node_modules/react/react.jsStart recursive parsing to process multiple files, which can be time consuming. Configure alias to directly use the full code file (react.min.js), skipping time-consuming recursive parsing.Code 4
    • Webpack looks for third-party modules. By default, it looks for node_modules in the current directory, and if it doesn’t find any, it looks for node_modules in the next level up. Given that all third-party modules are in node_modules, this upward lookup process is unnecessary. Resolve. Modules is configured to go directly to the current node_modules. Code 5
module.exports = {
    / /... Omit other configurations
    module: {rules: [{test:/\.js$/,
            use:['babel-loader']./ / code 1
            include:path.resolve(__dirname,'src'),}],/ / code 2
        noParse:/jquery|lodash/
    },
    resolve: {/ / code 3
        extensions: ['ts'.'vue']./ / code 4
        alias: {'react':path.resolve(__dirname,'./node_modules/react/dist/react.min.js')},5 / / code
        module:[path.resolve(__dirname,'node_modules')].}}Copy the code

5.3 DllPlugin

  • Every time we build a project, we rebuild all of our business code and the code of the third party modules, which is very time consuming. In most cases, the code for third-party modules is unchanged except for updates and upgrades. If each build package only deals with our business code and third-party modules are packaged into a dynamically linked library, this is where DLLS come in.
  • To do this, you need two plug-ins [DllPlugin] [DllReferencePlugin], both of which are built-in plug-ins for WebPack and do not need to be installed
    • DllPlugin: This plugin can separate the specified third-party library code, package the specified third-party dependencies into a bundle DLL, and produce a manifest.json file.
    • DllReferencePlugin: This plugin references the generated DLL files to the required precompiled dependencies
/ / DLL. Config. Js file
const webpack = require('webpack')
const path = require('path')
module.exports = {
    mode:'production'.entry: {// Third-party libraries
        react: ['react'.'react-dom'.'react-redux'].vendor: ['jquery'.'lodash'],},output: {// Specify the name of the output file and where the output file should be.
        filename:'[name].dll.js'.// Where output is level with dist, the dist directory may be cleaned by CleanWebpackPlugin or output.clean
        path:path.resolve(__dirname,'dll'),
        // The variable name attached to the global variable must match the name of the dllPlugin.
        library:'[name]_dll',},plugins: [new webpack.DllPlugin({
            /* This field is also the value of the name field in the output manifest.json file. * /
            name:'[name]_dll'.// Describes the file name of the manifest.json output of the dynamic link library
            path:path.join(__dirname,'dll'.'[name].manifest.json')]}})Copy the code
// webpack.config.js
// Package version: [email protected]
const webpack = require('webpack')
const path = require('path')
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
    / /... Other configuration
    plugins: [new webpack.DllReferencePlugin({
            manifest:path.resolve(__dirname,'./dll/vendor.manifest.json')}),new webpack.DllReferencePlugin({
            manifest:path.resolve(__dirname,'./dll/react.manifest.json')}),// Index. HTML does not include third-party DLLS.
        // Add-asset-html-webpack-plugin can dynamically do this for us by automatically introducing DLL files into HTML
        new AddAssetHtmlWebpackPlugin({
            filepath:path.resolve(__dirname,'./dll/vendor.dll.js'),
            publicPath:'/'
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath:path.resolve(__dirname,'./dll/react.dll.js'),
            publicPath:'/'}})]Copy the code

5.4 Packaging Thread – Loader for Multiple Processes

  • When the project is large and the amount of code is increasing, each build packaging will be slow, so multithreaded packaging can be used to speed up the packaging. Because of thread consumption, the packaging speed is slower when there are fewer files, so it is not recommended to use it when there are fewer files.
  • Common multithreaded packaging has thread-loader, happypack, parallel webpack, happypack now maintain less, thread-loader is officially launched more stable.
Installation package version: Thread-loader@3.04.
module.exports = {
    / /... Other configuration
    module: {rules:[
            {
                test:/\.js$/,
                use:[
                    {
                        loader:'thread-loader'.options: {workers:2.// Number of processes started, default is (number of CPU cores -1)
                            workParallelJobs:30.// The number of parallel executions in the process. Default is 20
                            poolTimeout:2000.// when idle, periodic deletion is limited to 500ms by default.}},/* Thread-loader must be executed last to enable multithread packaging for loader after thread-loader, such as babel-loader. * /
                    'babel-loader']},]}}Copy the code

5.5 Code Splitting

  • In the process of code segmentation, only necessary resources should be loaded as far as possible at each packaging, and lazy loading should be adopted if the priority is not too high to ensure the loading speed of the first screen of the page. Through this technology, the code can be divided according to specific rules, and it is not necessary to load all at once, but to load on demand.
  • Code segmentation solves the problem of the size of the first screen loading, but it also brings new problems, such as: which modules to be segmented, how to manage the resources after segmentation, these are to be strictly controlled.
  • There are two ways to split code

1.optimization.SplitChunks

  • SplitChunks are used more declaratively, by setting extraction conditions, modes, and volumes, and when certain modules meet these conditions, they will be extracted.
module.exports = {
    / /... Other configuration
    optimization: {splitChunks: {// Split configuration rules
            chunks:'all'.// Valid values :async (default),all,initial
            minSize:30*1024.// Files larger than 3KB are split
            minChunks:2.// The partition must be referenced at least twice.
            maxAsyncRequests:5.// Split up to 5 asyncs
            maxInitialRequest:3.// Limit the number of split entries. The maximum number of concurrent requests cannot exceed 3
            // About cacheGroups: A cache group exists temporarily after the conditions are met and is split based on its configuration rules.
            cacheGroups: {vendors: {test:/[\\/]node_modules[\\/]/.// Whether the imported library is in node_modules.
                    priority:-10.// Priority. A larger value indicates a higher priority
                    // True indicates that the current chunk contains modules split from the bundle and will be reused instead of generating new modules.
                    reuseExistingChunk:true.filename:'vendors.js'.// Reset package name
                },
                default: {// For configurations that do not match vendors rules, follow the rules here
                    priority: -20.reuseExistingChunk:true.filename:'common.js'.minChunks:2,}}}}}Copy the code

2. Dynamic load import()

  • Unlike the IMPORT syntax of ES6, modules and dependencies loaded through the import function are loaded asynchronously and return a Promise object. In addition, webpack import() can appear anywhere, such as inside the body of a function, or inside a block (if), so it is very dynamic. It is often used to render components when switching routes, reducing the load of resources on the first screen.
  • By default, packaged files will be [id].js, such as 0.js,1.js. You can change the default behavior by adding magic comments :/*webpackChunkName:”lodash”*/.
if(true) {// appears in the block
    import(/*webpackChunkName:"lodash"*/'lodash').then(({default: _}) = >{
        console.log(_.join('hello'.'world'))})}Copy the code

5.6 Externals

  • When you reference a library and do not want to package it with webPack, without affecting the use of CMD,AMD,ES6Module programs, you can configure Externals to do this.
  • Externals prevents certain imported packages from being added to the bundle, instead fetching these external extensions at run time, such as JQ in the CDN.
/ * need to be in the root directory of the HTML files manually introduction: < script SRC = "https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js" > < / script > * /
module.exports = {
/ /... Other configuration
    externals: {// The imported jquery is replaced with the global variable jquery. In other words, if it is a string, it can be treated as global.
        jquery:'jQuery'}}// add jquery to the js file
import $ from 'jquery'
const dom = $('div'The $()'body').append(dom.html('hello world'))
Copy the code

Webpack analysis tools

WebpackDashboard

  • Webpack outputs some package-related information in the console after each build, which is not intuitive and readable. The WebPack Dashboard command line visualization tool can better display these information.
// webpack.config.js
const DashboardPlugin = require('webpack-dashboard/plugin')
module.exports = {
    plugins: [new DashboardPlugin()
    ]
}

// package.json command line to add webpack-dashboard
{
    "scripts": {"dev":"webpack-dashboard -- webpack-dev-server"}}Copy the code

SMP(SpeedMeasureWebpackPlugin)

  • Pass when webPack builds slowly and the reason is unknownspeed-measure-webpack-pluginPlugins help you analyze the time spent by various loaders and plugins during the entire WebPack packaging process, and help identify performance bottlenecks in the build process.
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin') 
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
    // WebPack configuration
})
Copy the code

WebpackBundleAnalyzer

  • In order to ensure a good experience, the volume of the bundle output needs to be continuously monitored to prevent unnecessary redundant modules from being added.webpack-bundle-analyzerIt can analyze the composition of a bundle and infer whether there are duplicate modules, unnecessary modules, etc.
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
    / /...
    plugins: [new BundleAnalyzerPlugin()
    ]
}
Copy the code