1

Write at the top

I have used webpack a lot in my work recently. I want to study it systematically. I found a tutorial on Geek Time. I followed the video on the tutorial and learned a lot. Although the video tutorial was published in 2019, it is a bit old and some knowledge is outdated, but it does not hinder learning. Those outdated knowledge should be understood, and you can search for alternatives. Learning resources: play with webpack , the following are my study notes.

Build tool

Front-end technology development, such as: React of jsx , Vue and Angular instructions, CSS pre-processor, the latest ES grammar, the browser does not recognize.

The role of the build tool is to convert the grammar (high-level grammar) that the browser can't recognize into the grammar (low-level grammar) that the browser can recognize.

Another function is to compress and obfuscate the code, which reduces the size of the code while making the code difficult to read.

webpack is a popular front-end construction tool.

webpack acquaintance 061374642a0e86

Configure the script script

After installing webpack webpack in the command line, there are two ways:

  1. Specify the path ./node_modules/.bin/webpack
  2. Use npx tool, npx webpack

These two methods are very troublesome to use. There is a simple way to use package.json in scripts , it can read the node_modules/.bin directory 061374642a0f59.

npm run build in the command line.

"scripts": {
  "build": "webpack"
}

Configuration file

webpack default configuration file webpack.config.js , by webpack --config to assign a profile.

For example, the configuration file of the production environment is webpack.config.prod.js , and the configuration file of the development environment is webpack.config.dev.js .

"scripts": {
  "build": "webpack --config webpack.config.prod.js",
  "build:dev": "webpack --config webpack.config.dev.js"
}

Core idea

webpack has 5 core concepts: entry , output , mode , loaders , plugins .

entry

The entry file when packaging, the default is ./src/index.js

entry : single entrance and multiple entrances.

Single entry is used for single page applications, entry is a string

module.exports = {
  entry: "./src/index.js",
};

Multi-entry is used for multi-page applications, entry is the object form

module.exports = {
  entry: {
    index: "./src/index.js",
    app: "./src/app.js",
  },
};

output

The packaged output file, the default is ./dist/main.js .

entry is a single entry, output can modify the parameters path and filename .

const path = require("path");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
};

entry is a multi-entry, output of filename needs to use [name] placeholder to specify the packaged name, corresponding to entry in key

const path = require("path");

module.exports = {
  entry: {
    index: "./src/index.js",
    app: "./src/app.js",
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].js",
  },
};

mode

It can be set to development , production , none , and the default is production .

  • development : development environment, set process.env.NODE_ENV value development . Turn on NamedChunksPlugin and NamedModulesPlugin
  • production : production, provided process.env.NODE_ENV value production . Open FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitonErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin and TerserPlugin
  • none : Do not open any optimization options
ps: development mode, the console will print out which module has hot update, and what is the path of this module. The plug-in opened in production

loaders

webpack only supports the two file types js and json loader is to process other files and add them to the dependency graph.

loader is a function that receives the source file as a parameter and returns the converted result.

Generally, loader named with -loader as the suffix, such as css-loader , babel-loader .

test is the specified matching rule, use is the name specified to use loader

module.exports = {
  module: {
    rules: [
      {
        test: /.less$/,
        use: ["style-loader", "css-loader", "less-loader"],
      },
    ],
  },
};

A loader Usually only one thing, parsing less when needed less-loader to less turn into css , since webpack not recognize css , need css-loader to css converted commonjs objects into js , and finally need style-loader to css Insert it into style on the page.

ps: loader usually has two ways, one is from left to right (similar to unix of pipe ), and the other is from right to left ( compose ). webpack selects compose loader once from right to left

plugins

Anything that loader can't do can be plugin . It is mainly used for file optimization, resource management, environment variable injection, and it affects the entire construction process.

Generally, plugin named with -webpack-plugin , and some of it -plugin a suffix of 061374642a1392.

const CleanWebpackPlugin = require("clean-webpack-plugin");

module.exports = {
  plugins: [new CleanWebpackPlugin()],
};

webpack resource analysis

Analyze es6

Need to install @babel/core , @babel/preset-env , babel-loader

.babelrc under the root directory, add @babel/preset-env to presets

{
  "presets": ["@babel/preset-env"]
}

webpack is configured in babel-loader

module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        use: "babel-loader",
      },
    ],
  },
};

There is also a way of configuration, do not use .babelrc file, configure it in use parameters options in

module.exports = {
  module: {
    rules: [
      {
        test: /.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
};

Resolution css

Need to install css-loader and style-loader .

module.exports = {
  moudle: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

Analyze less

Need to install less-loader , css-loader , style-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: ["style-loader", "css-loader", "less-loader"],
      },
    ],
  },
};

Parse pictures and fonts

Need to install file-loader

module.exports = {
  module: {
    rules: [
      {
        test: /.(png|jpeg|jpg|gif)$/,
        use: ["file-loader"],
      },
      {
        test: /.(woff|woff2|eot|ttf|otf)$/,
        use: ["file-loader"],
      },
    ],
  },
};

url-loader

url-loader can convert smaller pictures into base64 .

module.exports = {
  module: {
    rules: [
      {
        test: /.(png|jpeg|jpg|gif)$/,
        use: {
          loader: "url-loader",
          options: {
            limit: 10240, // 小于 10k 图片,webpack 在打包时自动转成 base64
          },
        },
      },
    ],
  },
};

Configuration vue

Configure vue development environment, need to install vue , vue-loader , vue-template-compiler

const { VueLoaderPlugin } = require("vue-loader");

module.export = {
  plugins: [new VueLoaderPlugin()],
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: "vue-loader",
      },
    ],
  },
};
ps: The vue-loader used here is the 15.x version. I have a problem installing the latest version 16.x , which has not been resolved.

Configuration react

To configure the react development environment, you need to install react , react-dom , @babel/preset-react

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
          },
        },
      },
    ],
  },
};

webpack File monitoring and hot update

File monitoring

Every time you modify the code, you need to build it manually, which affects development efficiency. webpack provides the role of file monitoring. When the monitoring is turned on, webpack will call the node module in fs to determine whether the file has changed. If there is a change, it will automatically rebuild and output a new file.

webpack There are two ways to open the monitoring mode: (requires manual refresh of the browser)

  • When starting the webpack command, bring the --watch parameter
"scripts": {
 "watch": "webpack --watch"
}
  • webpack.config.js is set in watch: true
module.exports = {
  watch: true,
};

File monitoring and analysis

webpack File monitoring is based on whether the last editing time of the file has changed.

It will save the modification time, and when the file is modified, it will be compared with the last modification time.

If it finds inconsistencies, it will not tell the listener immediately, but cache the file modification first, and wait for a period of time. If there are other files that change during this period, it will list the files in this period of time. Build together. The waiting time is aggregateTimeout .

module.exports = {
  watch: true,
  watchOptions: {
    ignored: /node_modules/,
    aggregateTimeout: 300,
    poll: 1000,
  }
}
  • watch : The default is false
  • watchOptions : Only when watch is true , it will take effect

    • ignored : The files or folders that need to be ignored for monitoring, the default is empty, and the node——modules will be improved.
    • aggregateTimeout : The waiting time after the file change is monitored, the default is 300ms
    • poll : polling time, 1s once

Hot update

Hot update requires two plug-ins, webpack-dev-server and HotModuleReplacementPlugin

Compared with watch , it does not output files, directly in the memory, so its construction is faster.

Hot update will only be used in development mode.

const webpack = require("webpack");
const path = require("path");

module.exports = {
  mode: "development",
  plugins: [new webpack.HotModuleReplacementPlugin()],
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    hot: ture,
  },
};

Configure commands in package.json

webpack4.x

"scripts": {
  "dev": "webpack-dev-server --open"
}

webpack5.x

"scripts": {
  "dev": "webpack server"
}
PS: webpack5.x and webpack-dev-server there is a conflict, you can not use --open open the browser.

HotModuleReplacementPlugin

The core of the hot update is HMR Server and HMR Runtime .

  • HMR Server : is the server side, used to notify the browser side of the js module through websocket
  • HMR Runtime : is the browser side, used to receive HMR Server , the browser side can see the .hot-update.json file

hot-module-replacement-plugin effects: webpack itself is constructed out of bundle.js itself is not provided with heat update, HotModuleReplacementPlugin effect is HMR Runtime injected into bundle.js , such bundle.js can HMR Server establish websocket communication capabilities. Once the disk where the file is modified, then HMR Server will have to modify js module websocket sent to HMR Runtime , then HMR Runtime to update the local code page, this method does not refresh your browser.

webpack-dev-server role: providing bundle server capacity is generated bundle.js by localhost://xxx to access methods are also provided at livereload (auto-refresh browser).

File fingerprint

What is file fingerprinting

File fingerprint refers to the suffix of the file name output after packaging. For example: index_56d51795.js .

Usually used for version management

File fingerprint type

  • hash : Related to the construction of the project, as long as the project file changes, the hash constructed project will change. If you use hash calculate, the hash value after each build is different. If the content of the file does not change, then it is impossible to achieve caching.
  • chunkhash : It is related to webpack packaged by chunk . Different entry will generate different chunkhash . The production environment will separate some public libraries from the source code and chunkhash separately. As long as the code of the public library is not changed, its hash value will not change, and caching can be implemented.
  • contenthash hash according to the content of the file. If the content of the file does not change, then contenthash does not change. Generally used for css resources, if css resource use chunkhash , then modify the js , css will change the resources, the cache will fail, so css use contenthash .

ps:

  1. Use hash scenes to combine mode to consider, if mode is development , using HMR case, use chunkhash is not suitable, you should use hash . When mode is production chunkhash should be used.
  2. js uses chunkhash find resources easily, because js a higher degree of resource relevance. css uses contenthash because css generally written based on different pages, css resources is not high, so there is no need to update css when other resources are modified.

js file fingerprint

Set output of filename , use [chunkhash] ,

const path = require("path");

module.exports = {
  output: {
    path: path.join(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js", // 取 hash 的前 8位
  },
};

css file fingerprint

Need to install mini-css-extract-plugin

style-loader inserts css into head of the mini-css-extract-plugin is extracted as a separate file, and they are mutually exclusive.

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name]_[contenthash:8].css",
    }),
  ],
};

Image file fingerprint

Need to install file-loader

Placeholder namemeaning
[ext]Resource suffix
[name]file name
[path]Relative file path
[folder]The folder where the file is located
[contenthash]hash file content, the default is md5
[hash]hash file content, the default is md5
[emoji]A random reference to the content of the file emoji

Pictures of hash and css/js of hash concept is not the same, the picture hash there is a picture of the contents of the decision.

module.exports = {
  module: {
    rules: [
      {
        test: /.(png|jpeg|jpg|gif)$/,
        use: {
          loader: "file-loader",
          options: {
            filename: "[folder]/[name]_[hash:8].[ext]",
          },
        },
      },
      {
        test: /.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              filename: "[name]_[hash:8].[ext]",
            },
          },
        ],
      },
    ],
  },
};

Code compression

Code compression is mainly divided into js compression, css compression, and html compression.

js compression

webpack built-in uglifyjs-webpack-plugin plug-in. By default, the files after packaging are compressed, and no additional configuration is required here.

You can install this plug-in manually, and you can set additional configurations. For example, to enable parallel compression, you need to set parallel to true

const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
  optimization: {
    minimizer: [
      new UglifyjsWebpackPlugin({
        parallel: true,
      }),
    ],
  },
};

css compression

Install optimize-css-webpack-plugin css preprocessor cssnano at the same time.

const OptimizeCssWebpackPlugin = require("optimize-css-webpack-plugin")
module.exports = {
  plugins: [
    new OptimizeCssWebpackPlugin({
      assetNameRegExp: /\.css$/g,
      cssProcessor: require("cssnano)
    })
  ]
}

html compression

Need to install html-webpack-plugin , by setting compression parameters.

ps: Multi-page applications need to write multiple new HtmlWebpackPlugin({...})
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
     template: path.join(__dirname, "src/search.html"),    //html 模版所在的位置,模版里面可以使用 ejs 的语法
      filename: "search.html",              // 打包出来后的 html 名称
      chunks: ["search"],           // 打包出来后的 html 要使用那些 chunk
      inject: true,                // 设置为 true,打包后的 js css 会自动注入到 HTML 中
      minify: {
        html5: true,
        collapseWhitespace: true,
        preserveLineBreaks: false,
        minifyCSS: true,
        minifyJS: true,
        removeComments: false
    }),
    new HtmlWebpackPlugin({...})
  ],
};

The minifyJS and minifyCSS HtmlWebpackPlugin parameter minify js ES6 syntax cannot be used) and css which are inlined in the html at the beginning.

chunks corresponds to entry in key . chunk you want to be injected automatically, write chunk chunks .

chunk difference between 061374642a1fc9, bundle , module

  • chunk : Each chunk is module , which can be divided into multiple chunk
  • bundle : The final file generated by packaging
  • module : webpack in js , css , pictures)

Automatically clean up the build directory

Every time during the build, a new file will be generated, resulting in more and more output

The most common cleaning method is rm . Before packaging, execute the rm -rf command to dist directory, and then pack it.

"scripts": {
  "build": "rm -rf ./dist && webpack"
}

The other is to use rimraf to delete.

To install rimraf , first dist directory before packaging, and then package it.

"scripts": {
  "build": "rimraf ./dist && webpack"
}

Although these two solutions can dist directory, they are not very elegant.

webpack provides clean-webpack-plugin , which will automatically clean up output.path under 061374642a2133.

const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  plugins: [new CleanWebpackPlugin()],
};

Auto-fill style prefix

Different browser manufacturers have not fully unified the standards for css display: flex , which should be written as display: -webkit-box in the webkit kernel.

Adding the kernel one by one during development will be a huge project.

In webpack , you can use loader to solve the problem of automatically css prefix of 061374642a219c.

Install postcss-loader and its plug-in autoprefixer .

autoprefixer is based can i use This site provides css be incomplete prefix compatibility.

autoprefixer is a post processor, which is different from the preprocessor. The preprocessor is processed during packaging, while autoprefixer is processed after the code has been processed and the style has been generated.

In webpack4.x installation postcss-loader@3.0.0 , autoprefixer@9.5.1 .

Method 1: directly configured webpack.config.js

webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "less-loader",
          {
            loader: "postcss-loader",
            options: {
              plugins: () => [
                require("autoprefixer")({
                  overrideBrowserslist: ["last 2 version", ">1%", "ios 7"], //最新的两个版本,用户大于1%,且兼容到 ios7
                }),
              ],
            },
          },
        ],
      },
    ],
  },
};

Method 2: uses the postcss configuration file postcss.config.js , and webpack.config.js directly writes postcss-loader .

webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "less-loader",
          "postcss-loader",
        ],
      },
    ],
  },
};

postcss.config.js

module.exports = {
  plugins: [
    require("autoprefixer")({
      overrideBrowserslist: ["last 2 version", ">1%", "ios 7"],
    }),
  ],
};

Method three: browser compatibility can write package.json in, postcss.config.js in just load autofixer can be.

webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "less-loader",
          "postcss-loader",
        ],
      },
    ],
  },
};

postcss.config.js

module.exports = {
  plugins: [require("autofixer")],
};

package.json

"browserslist": {
  "last 2 version",
  "> 1%",
  "ios 7"
}

Resource inlining

Resource inlining needs to be used in some scenarios, the common ones are:

  1. The initialization script of the page frame
  2. Reduce http network requests, change some small pictures into base64 content into the code, less requests
  3. css inline to increase page experience
  4. js executed as soon as possible, such as the REM plan

html inline

In a multi-page project, head there are many common label, such as meta , want to improve maintenance, it will extract into a template, and then reference room.

Install raw-loader@0.5.1

<head>
  ${require("raw-loader!./meta.html")}
</head>

js inline

In REM program, you need as soon as possible html label font-size , then this js will load as soon as possible, to perform.

<head>
  <script>
    ${require('raw-loader!babel-loader!./calc-root-fontsize.js')}
  </script>
</head>

css inline

In order to have a better experience and avoid page flickering, you need to inline the css head .

Install html-inline-css-webpack-plugin , this plug-in needs to be placed behind html-webpack-plugin

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlInlineCssWebpackPlugin = require("html-inline-css-webpack-plugin");

module.exports = {
  module: {
    rules: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name]_[contenthash:8].css",
    }),
    new HtmlWebpackPlugin(),
    new HtmlInlineCssWebpackPlugin(),
  ],
};

You need to css into a separate file first.

ps: The difference between style-laoder and html-inline-css-webpack-plugin

  • style-loader : Inserting the style is a dynamic process, the packaged source code will not have the style tag, and the style tag is dynamically inserted js
  • html-inline-css-webpack-plugin : Insert css into the style tag of the page during construction

Multi-page application

Each page corresponds to a entry , and at the same time corresponds to a plugins in html-webpack-plugin .

This method has a shortcoming, each additional entry will increase a html-webpack-plguin .

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    index: "./src/index.js",
    search: "./src/search.js",
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new HtmlWebpackPlugin({ template: "./src/search.html" }),
  ],
};

With the help of glob a universal configuration can be realized.

Install glob , and all pages must be placed under src , and the entry file must be called index.js .

const path = require("path");
const glob = require("glob");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const setMPA = () => {
  const entry = {};
  const htmlWebpackPlugins = [];
  const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"));

  entryFiles.forEach((entryFile) => {
    const match = entryFile.match(/src\/(.*)\/index\.js/);
    const pageName = match && match[1];

    entry[pageName] = entryFile;
    htmlWebpackPlugins.push(
      new HtmlWebpackPlugin({
        template: path.join(__dirname, `src/${pageName}/index.html`),
        filename: `${pageName}.html`,
        chunks: [pageName],
        inject: true,
        minify: {
          html5: true,
          collapseWhitespace: true,
          preserveLineBreaks: false,
          minifyCSS: true,
          minifyJS: true,
          removeComments: false,
        },
      })
    );
  });

  return {
    entry,
    htmlWebpackPlugins,
  };
};

const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
  entry,
  plugins: [].concat(htmlWebpackPlugins),
};

sourceMap use 061374642a2655

The code released to the generation environment should be compressed and obfuscated, but after compression and obfuscation, reading the code is like reading a book.

sourceMap provides the mapping relationship between the compressed code and the source code.

sourceMap in the development environment, and closed in the online environment. When troubleshooting online, source map to the error monitoring system.

source map keywords

  • eval : Use eval package the module code
  • source map : Generate map file
  • cheap : does not contain column information
  • inline : .map as DataURI , without generating .map files separately
  • module : contains loader of sourcemap

source map type

devtoolFirst buildSecond buildIs it suitable for production environmentTargetable code
(none)++++++yesThe final output code
eval++++++nowebpack generated code (a module)
cheap-eval-source-map+++noThe code after loader conversion (only lines can be seen)
cheap-module-eval-source-mapo++noSource code (only lines can be seen)
eval-source-map--+noSource code
cheap-source-map+oyes( loader conversion can only see the line)
cheap-module-source-mapo-yesSource code (only lines can be seen)
inline-cheap-source-map+-noThe code after loader conversion (only lines can be seen)
inline-cheap-module-source-mapo-noSource code (only lines can be seen)
source-map----yesSource code
Inline-source-map----noSource code
hidden-source- map----yesSource code

Extract page public resources

In the project, some basic libraries are used in multiple pages, such as react , react-dom , and a common code. When packaging these will be packaged into the final code, which is wasteful, and after packaging The volume is also relatively large.

webpack4 built-in SplitChunksPlugin plug-in.

chunks parameter description:

  • async Asynchronously imported libraries are separated (default)
  • initial Synchronously imported libraries are separated
  • all All imported libraries are separated (recommended)

Pulled out the name of the base library cacheGroups in the filename into html-webpack-plugin in chunks in, it is automatically imported:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
        chunks: ["vendors", "commons", "index"], //打包出来后的 html 要使用那些 chunk
      })
    );
  ],
  optimization: {
    splitChunks: {
      chunks: "async",
      minSize: 30000, // 抽离的公共包最小的大小,单位字节
      maxSize: 0, // 最大的大小
      minChunks: 1, // 资源使用的次数(在多个页面使用到), 大于1, 最小使用次数
      maxAsyncRequests: 5, // 并发请求的数量
      maxInitialRequests: 3, // 入口文件做代码分割最多能分成3个js文件
      automaticNameDelimiter: "~", // 文件生成时的连接符
      automaticNameMaxLength: 30, // 自动自动命名最大长度
      name: true, //让cacheGroups里设置的名字有效
      cacheGroups: {
        //当打包同步代码时,上面的参数生效
        vendors: {
          test: /[\\/]node_modules[\\/]/, //检测引入的库是否在node_modlues目录下的
          priority: -10, //值越大,优先级越高.模块先打包到优先级高的组里
          filename: "vendors.js", //把所有的库都打包到一个叫vendors.js的文件里
        },
        default: {
          minChunks: 2, // 上面有
          priority: -20, // 上面有
          reuseExistingChunk: true, //如果一个模块已经被打包过了,那么再打包时就忽略这个上模块
        },
      },
    },
  },
};

tree shaking (optimization for tree shaking)

There are multiple methods in a module, the methods used will be packaged into bundle , and the methods not used will not be packaged.

tree shaking is to pack only the used methods into bundle , and the other unused ones will be uglify stage.

webpack supported by default, in .babelrc set in modules: false can. production stage is enabled by default.

The syntax must be ES6 , and CJS is not supported.

ps: If ES6 turn into ES5 , while also open tree shaking , need .babelrc set in module: false , otherwise babel default will ES6 turn into CJS written specification, so it can not be tree shaking .

DCE(deal code elimination)

DEC full name of deal code elimination, which means dead code elimination in Chinese. There are mainly the following three types:

  1. Code will not be executed
if (false) {
  console.log("代码不会被执行");
}
  1. The result of code execution will not be used
function a() {
  return "this is a";
}
let A = a();
  1. Code only writes but not reads
let a = 1;
a = 2;

tree shaking principle

Perform static analysis on the code. In the compilation stage, whether the code is used or not is determined. You cannot analyze which code is used when the code is running. tree shaking will mark the unused code with comments. These useless codes are erased in the uglify

Utilize the features of the ES6

  • Can only appear as a statement at the top level of the module
  • import can only be a string constant
  • import binding is immutable

Delete useless css

  • purgecss-webpack-plugin : Traverse the code and identify the css class

    • mini-css-extract-plugin with 061374642a2d3c
  • uncss : html need jsdom loaded by all styles postCSS resolved by document.querySelector identified in html nonexistent file selector which
const PurgecssPlugin = require("purgecss-webpack-plugin");

const PATHS = {
  src: path.join(__dirname, "src"),
};

module.exports = {
  plugins: [
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
    }),
  ],
};

scope hoisting usage and principle analysis

Enough code will produce a large number of closures, as shown in the following figure:

1.png

When an external module is introduced, it will be wrapped in a function. When more modules are introduced, a large amount of function wrapping code will be generated, resulting in a larger volume. During operation, the more function scopes are created, the memory overhead will also increase.

  • The module converted by webpack will be wrapped in one layer
  • import will be converted to __webpack_require

analyze

  1. What is packaged is a IIFE (anonymous closure)
  2. modules is an array, each item is a module initialization function
  3. __webpack_require used to load the module, return module.exports
  4. Start the program through WEBPACK_REQUIRE_METHOD(0)

scope hoisting principle

Put the code of all modules in a function scope in the order of reference, and then appropriately rename some variables to prevent variable name conflicts.

Module calls are in order. The a module calls the b module. Because of the function package, a module and the b module does not matter. If the package code is eliminated, the modules need to be b according to the module reference order, and the 061374642a2f2c module must be placed in a Before the module, the b module can be a module.

Through scope hoisting can reduce function declaration and memory overhead. production stage is enabled by default.

Must be ES6 syntax, CJS does not support.

ps: scope hoisting turns multiple scopes into one scope. When the module is quoted more than 1 times, it has no effect. If a module is cited more than 1 times, then the code of this module will be inlined multiple times, thereby increasing the size of the packaged file.

Use ESLint

It is ESLint that can unify the team's code style and help find errors.

Two methods of use:

  • Integration with CI/CD
  • Integration with webpack

webpack and CI/CD Integration

Need to install husky .

Add scripts and pass lint-staged check and modify the file.

"scripts": {
  "precommit": "lint-staged"
},
"lint-staged": {
  "linters": {
    "*.{js,less}": ["eslint --fix", "git add"]
  }
}

webpack and ESLint Integration

Use eslint-loader , the build is to check the js specification

Install the plug-in babel-eslint , eslint , eslint-config-airbnb , eslint-config-airbnb-base , eslint-loader , eslint-plugin-import , eslint-plugin-jsx-ally , eslint-plugin-react .

Create a new file .eslintrc.js

module.exports = {
  parser: "babel-eslint",
  extends: "airbnb",
  env: {
    browser: true,
    node: true,
  },
  rules: {
    indent: ["error", 2],
  },
};

webpack.config.js File configuration eslint-loader

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ["babel-loader", "eslint-loader"],
      },
    ],
  },
};

Code segmentation and dynamics import

For large web applications, putting all the code in one file is obviously not effective enough, especially when some of your code blocks are used in some special circumstances.

webpack has a function to divide your code into chunks (chunks), and load them when the code runs to the point where they are needed.

scenes to be used:

  • Extract the same code into a shared block
  • Lazy loading of scripts makes the initial download code smaller

Lazy loading of js script

  • CJSrequire.ensure
  • ES6 : import (Currently there is no native support, bable conversion is required)

Install @bable/plugin-syntax-dynamic-import plugin

.babelrc file:

{
  plugins: ["@bable/plugin-syntax-dynamic-import"];
}

example:

class Index extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      Text: null,
    };
  }
  loadComponent = () => {
    import("./text.js").then((Text) => {
      this.setState({ Text: Text.default });
    });
  };
  render() {
    const { Text } = this.state;
    return (
      <div className="search">
        react1
        {Text && <Text />}
        <div onClick={this.loadComponent}>点我</div>
        <img src={img} alt="" />
      </div>
    );
  }
}

One thing to note here, when cacheGroups is minChunks set to 1 , the lazy loading script set above will not take effect, because import is statically analyzed when it is loaded.

cacheGroups: {
  commons: {
    name: "commons",
    chunks: "all",
    priority: -20, //值越大,优先级越高.模块先打包到优先级高的组里
    minChunks: 1,
  }
}

Multi-process/multi-instance: parallel compression

Method 1: Use webpack-parallel-uglify-plugin plug-in

const WebpackParalleUglifyPlugin = require("webpack-parallel-uglify-plugin");

module.exports = {
  plugins: [
    new WebpackParalleUglifyPlugin({
      uglifyJs: {
        output: {
          beautify: false,
          comments: false,
        },
        compress: {
          warnings: false,
          drop_console: true,
          collapse_vars: true,
          reduce_vars: true,
        },
      },
    }),
  ],
};

Method 2: Use uglifyjs-webapck-plugin enable parallel parameters

const UglifyJsPlugin = require("uglifyjs-webpack-plugin")

modules.exports = {
  plugins: [
    UglifyJsPlugin: {
      warnings: false,
      parse: {},
      compress: {},
      mangle: true,
      output: null,
      toplevel: false,
      nameCache: null,
      ie8: false,
      keep_fnames: false,
    },
    parallel: true
  ]
}

Method three: terser-webpack-plugin enable parallel parameters ( webpack4 recommended)

const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4,
      }),
    ],
  },
};

Speed up the build

Idea: react , react-dom , redux , react-redux basic package and business basic package into one file

Methods: DLLPlugin subcontracting, DLLReferencePlugin of manifest.json references

Use DLLPlugin for subcontracting

const path = require("path");
const webpack = require("webpack");

module.exports = {
  context: process.cwd(),
  resolve: {
    extensions: [".js", ".jsx", ".json", ".less", ".css"],
    modules: [__dirname, "nodu_modules"],
  },
  entry: {
    library: ["react", "react-dom", "redux", "react-redux"],
  },
  output: {
    filename: "[name].dll.js",
    path: path.resolve(__dirname, "./build/library"),
    library: "[name]",
  },
  plugins: [
    new webpack.DllPlugin({
      name: "[name]",
      path: "./build/library/[name].json",
    }),
  ],
};

Introduced at webpack.config.js

module.exports = {
  plugins: [
    new webapck.DllReferencePlugin({
      manifest: require("./build/library/manifest.json"),
    }),
  ],
};

In the project uses webpack4 , for dll dependent not so big, use dll relatively lift it is not particularly obvious, but hard-source-webpack-plugin can greatly enhance the secondary building.
But from the actual front-end factory, dll is still necessary. For a team, basically use the same technology stack, either react or vue . At this time, the usual practice is to type the public framework into a common bundle file for all projects to use. dll can well meet this scenario: multiple npm packages are typed into a common package. dll in the subcontracting plan in the team is still very valuable.

splitChunks can also do DllPlugin , but it is recommended to use splitChunks to extract the public js files between pages, because splitChunks to extract the basic package, it still takes construction time. If it is DllPlugin only needs to be pre-compiled once, and the subsequent basic package Time can be omitted.

Speed up the second build

Method 1: Use terser-webpack-plugin open the cache

module.exports = {
  optimization: {
    minimizer: [
      new TerserWebpackPlugin({
        parallel: true,
        cache: true,
      }),
    ],
  },
};

Method 2: Use cache-loader or hard-source-webpack-plugin

module.exports = {
  plugins: [new HardSourceWebpackPlugin()],
};

Reduce the build target

For example, babel-loader does not resolve node_modules

module.exports = {
  rules: {
    test: /\.js$/,
    loader: "happypack/loader",
    // exclude: "node_modules"
    /-- 或者 --/
    // include: path.resolve("src"),
  }
}

Reduce file search scope

  • Optimize resolve.modules configuration (reduce module search level)
  • Optimize resolve.mainFields configuration

    • First find package.json in main field specifies the file -> find the root directory index.js -> Find lib/index.js
  • Optimize resolve.extensions configuration

    • To find the module path, import xx from "index" will first find the suffix of .js
  • Reasonable use alias
module.exports = {
  resolve: {
    alias: {
      react: path.resolve(__dirname, "./node_modules/react/dist/react.min.js"),
    },
    modules: [path.resolve(__dirname, "node_modules")], // 查找依赖
    extensions: [".js"], // 查找模块路径
    mainFields: ["main"], // 查找入口文件
  },
};

uccs
759 声望89 粉丝

3年 gis 开发,wx:ttxbg210604