Write it up front

Recently, I have been using Webpack a lot in my work, so I want to learn it systematically. I found the tutorial of Geek Time and learned it by following the video, and gained a lot. Although the video tutorial came out in 2019, it is a little old and some of the knowledge is outdated, but it does not prevent learning. All outdated knowledge should be known and searched for alternatives. Learning resources: Playing with Webpack, here are my learning notes.

Build tools

The React JSX, Vue and Angular directives, CSS preprocessors, the latest ES syntax, etc., are constantly evolving that browsers don’t recognize.

What the build tool does is convert the syntax that the browser does not recognize (advanced syntax) into the syntax that the browser does recognize (low-level syntax).

It also confuses code compression, making it harder to read while reducing the size of the code.

Webpack is now a popular build tool on the front end.

I metwebpack

configurationscriptThe script

Once webPack is installed, there are two ways to use webpack on the command line:

  1. The specified path./node_modules/.bin/webpack
  2. usenpxTools,npx webpack

These two methods are quite cumbersome to use. There is an easy way to use scripts in package.json to read commands in node_modules/.bin.

Run NPM run build from the command line.

"scripts": {
  "build": "webpack"
}
Copy the code

The configuration file

The default configuration file for Webpack, webpack.config.js, can be specified via webpack –config.

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

"scripts": {
  "build": "webpack --config webpack.config.prod.js"."build:dev": "webpack --config webpack.config.dev.js"
}
Copy the code

The core concept

Webpack has five core concepts: Entry, Output, Mode, loaders, and plugins.

entry

/ SRC /index.js by default./ SRC /index.js

Entry is available in two ways: single entry and multiple entry.

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

module.exports = {
  entry: "./src/index.js"};Copy the code

Multi-entry is used for multi-page applications. Entry is an object

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

output

The packaged output file is./dist/main.js by default.

Entry is a single entry. Output can be modified by modifying path and filename.

const path = require("path");

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

There are multiple entry entries. Filename of output needs to use [name] placeholder to specify the name after packaging, which corresponds to the key in the entry

const path = require("path");

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

mode

It can be set to development, production, or None. The default is production.

  • development: Development environment, Settingsprocess.env.NODE_ENVThe value ofdevelopment. openNamedChunksPluginandNamedModulesPlugin
  • production: Production environment, Settingsprocess.env.NODE_ENVThe value ofproduction. openFlagDependencyUsagePlugin.FlagIncludedChunksPlugin.ModuleConcatenationPlugin.NoEmitonErrorsPlugin.OccurrenceOrderPlugin.SideEffectsFlagPluginandTerserPlugin
  • none: No optimization options are enabled

Ps: During the hot update phase of the code, the two plug-ins opened in development mode will print out on the console which module has been hot updated and what is the path of this module. Plug-ins opened in Production mode compress the code.

loaders

Webpack only supports JS and JSON file types. Loader is used to process other files and add them to dependency diagrams.

Loader is a function that takes the source file as an argument and returns the converted result.

Generally, loaders are named with the -loader suffix, such as CSS-loader and babel-loader.

Test specifies the matching rule, and use specifies the name of the loader to use

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

A loader usually does only one thing. When parsing less, it needs to use less-Loader to convert less into CSS. Since WebPack cannot recognize CSS, it needs to use CSS-Loader to convert CSS into CommonJS objects and put them into JS. Finally, you need to use style-loader to insert the CSS into the style of the page.

Ps: Loaders are typically composed in two ways, one from left to right (similar to Unix pipes) and the other from right to left (compose). Webpack selects Compose and executes the loader once from right to left

plugins

Plugin is used for file optimization, resource management, environment variable injection, and the entire build process.

Plugins are usually named with the suffix -webpack-plugin, and some are named with the suffix -plugin.

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

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

webpackResource parsing

parsinges6

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

Create a new. Babelrc file under the root directory and add @babel/preset-env to the presets

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

Configure babel-loader in webpack

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

There is another way to configure it, instead of using the. Babelrc file, configure it in the options parameter of use

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

parsingcss

You need to install CSS-loader and style-loader.

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

parsingless

Less-loader, CSS-loader, and style-loader need to be installed

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

Parse images and fonts

The file-loader needs to be installed

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

url-loader

Url-loader can convert smaller images to Base64.

module.exports = {
  module: {
    rules: [{test: /.(png|jpeg|jpg|gif)$/,
        use: {
          loader: "url-loader".options: {
            limit: 10240.// For images smaller than 10K, WebPack automatically converts to Base64 when packing},},},],},};Copy the code

configurationvue

To configure the VUE development environment, install VUE, VUE-Loader, and vue-template-Compiler

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

module.export = {
  plugins: [new VueLoaderPlugin()],
  module: {
    rules: [{test: /\.vue$/,
        use: "vue-loader",},],},};Copy the code

Ps: The vue-Loader used here is version 15.x. I have a problem installing the latest version 16.x, which has not been solved.

configurationreact

Install react, react-dom, @babel/preset-react to configure the React development environment

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

webpackFile monitoring and hot update

File to monitor

Every time the code is modified, it needs to be built manually, affecting development efficiency. Webpack provides file listening. When listening is enabled, WebPack calls the FS module in Node to determine if the file has changed, and if it has, it will automatically rebuild and output the new file.

Webpack enables listening mode in two ways :(manually refresh the browser)

  • Start thewebpackWhen ordered, take it--watchparameter
"scripts": {
 "watch": "webpack --watch"
}
Copy the code
  • webpack.config.jsSet in thewatch: true
module.exports = {
  watch: true};Copy the code

File listening analysis

The Webpack file listens to see if the last edit time of the file has changed.

It saves the modified time, and when the file is modified, it compares it with the last modified time.

If it finds an inconsistency, it does not immediately tell the listener. Instead, it caches the changes to the file, waits for a period of time, and builds a list of files from that time together if other files change during that time. This waiting time is called the aggregateTimeout.

module.exports = {
  watch: true.watchOptions: {
    ignored: /node_modules/, aggregateTimeout:300.poll: 1000,}}Copy the code
  • watchDefault for:false
  • watchOptions: onlywatchfortrue, takes effect
    • ignored: Files or folders to be listened to are ignored. The default value is empty and ignoredThe node - modulesPerformance will improve.
    • aggregateTimeout: Time to wait after listening for file changes, default300ms
    • poll: Polling time,1sAt a time

Hot update

Hot update need webpack – dev server and HotModuleReplacementPlugin use behind two plug-ins.

Compared to Watch, it doesn’t output files and goes straight to memory, so it builds faster to read.

Hot updates are only 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,
  },
};
Copy the code

Configure commands in package.json

webpack4.x

"scripts": {
  "dev": "webpack-dev-server --open"
}
Copy the code

webpack5.x

"scripts": {
  "dev": "webpack server"
}
Copy the code

Ps: Webpack5. x conflicts with webpack-dev-server. –open cannot be used to open the browser.

HotModuleReplacementPlugin

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

  • HMR Server: is the server used to changejsModule bywebsocketNotifies the browser side of the message
  • HMR Runtime: is the browser side, used for receivingHMR ServerThe passed module data is visible to the browser.hot-update.jsonfile

Hot-module-altern-plugin functions: Webpack itself to build up the bundle of js itself is not have hot update, the role of HotModuleReplacementPlugin is HMR Runtime into bundles. Js. Enables bundle.js to establish websocket communication with HMR Server. Once a file on disk is modified, HMR Server sends the js module with the modification to HMR Runtime via websocket. HMR Runtime then updates the page code locally without refreshing the browser.

Webpack-dev-server provides the ability of the bundle server to generate bundle.js that can be accessed via localhost:// XXX, as well as livereload (browser auto-refresh).

File fingerprint

What is a document fingerprint

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

It is usually used for version management

File fingerprint type

  • hash: Relates to the construction of the project, whenever the project files change, the construction of the projecthashThe value will change. usinghashComputatively, the hash value is different after each build, and if the contents of the file have not changed, it is not possible to cache this way.
  • chunkhashAnd:webpackpackagedchunkRelevant, differententryIt’s going to be differentchunkhash. The production environment will separate some common libraries from the source code and use them separatelychunkhashBuild, as long as the code of the common library does not change, its hash value does not change, can implement caching.
  • contenthash: Defined according to the file contenthash, the file content remains unchanged, thencontenthashThe same. Commonly used incssResources, ifcssResource usechunkhash, so modifiedjs.cssThe resource would change, the cache would fail, socssusecontenthash.

ps:

  1. usehashThe scene should be combinedmodeLet’s think about ifmodeisdevelopmentAnd in the use ofHMRIn case of usechunkhashIs not suitable, should be usedhash. whilemodeisproductionShould be usedchunkhash.
  2. jsusechunkhashIt’s easy to find resources becausejsThe resource correlation degree is higher.cssusecontenthashbecausecssIt’s usually written on different pages,cssThe correlation between resources is not high, so there is no need to update other resources when they are modifiedcss.

jsFile fingerprint

To set filename for output, use [chunkhash],

const path = require("path");

module.exports = {
  output: {
    path: path.join(__dirname, "dist"),
    filename: "[name]_[chunkhash:8].js".// Take the first eight bits of the hash}};Copy the code

cssFile fingerprint

The Mini-CSS-extract-plugin needs to be installed

Style-loader inserts CSS into the head of a page, and mini-css-extract-plugin extracts into a separate file. 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",})]};Copy the code

Image file fingerprint

The file-loader needs to be installed

Placeholder name meaning
[ext] Resource name extension
[name] The file name
[path] The relative path of the file
[folder] The folder where the file resides
[contenthash] document-contenthashThe default ismd5
[hash] document-contenthashThe default ismd5
[emoji] A random reference to the contents of the fileemoji

The concept of image hash is different from that of CSS/JS. The hash of an image is determined by the content of the image.

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]",},},],},},};Copy the code

Code compression

Code compression is mainly divided into JS compression, CSS compression, HTML compression.

jsThe compression

Webpack has built-in uglifyjs-webpack-plugin, default packaged files are compressed, there is no need for additional configuration.

You can install the plug-in manually, and you can set additional configurations, such as parallel compression, to true

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

cssThe compression

Install the optimize- CSS -webpack-plugin and CSS preprocessor CSsnano.

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

htmlThe compression

The htML-webpack-plugin needs to be installed by setting compression parameters.

New HtmlWebpackPlugin({… })

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

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
     template: path.join(__dirname, "src/search.html"),    // The location of the HTML template, which can use EJS syntax
      filename: "search.html".// The packaged HTML name
      chunks: ["search"].// The packaged HTML will use those chunks
      inject: true.// Set to true, the packed JS CSS is automatically injected into the HTML
      minify: {
        html5: true.collapseWhitespace: true.preserveLineBreaks: false.minifyCSS: true.minifyJS: true.removeComments: false
    }),
    newHtmlWebpackPlugin({... })]};Copy the code

HtmlWebpackPlugin minifyJS and minifyCSS in minify are used to compress JS and CSS inline in HTML from the start (ES6 syntax is not allowed).

Chunks correspond to keys in the entry. Which chunk you want to automatically inject will be written to the chunks.

Differences between Chunk, bundle, and Module:

  • chunk: eachchunkIs more thanmoduleComposition, which can be divided into multiple parts by codechunk
  • bundle: Final file generated by packaging
  • module:webpackModule (js,css, the picture)

Automatically clean up the build directory

Each time a build is made, new files are generated, resulting in more and more output files in the output directory.

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

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

Another option is to use Rimraf for deletion.

To install Rimraf, delete the dist directory before packing, and then pack.

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

Both of these solutions clean up the DIST directory, but they’re not very elegant.

Webpack provides the clean-webpack-plugin, which automatically cleans files under output.path.

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

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

Autocomplete style prefixes

Different browser vendors have different standards for implementing CSS features. For example, display: flex is written as display: -webkit-box in the WebKit kernel.

Adding cores one by one during development would be a huge undertaking.

Webpack can use loader to solve the problem of automatically completing CSS prefixes.

Install postCSS-Loader and its plug-in autoprefixer.

Autoprefixer is prefixed by the CSS compatibility provided by can I Use.

Autoprefixer is a post-processor, which is different from a preprocessor, which is processed at packaging time, and autoprefixer, which is processed after the code has been processed and the style has been generated.

Install [email protected], [email protected] in WebPack 4.x.

Method 1: Directly configure the configuration in 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"].// The latest two versions, more than 1% users and compatible with ios7}),],},},],},],},},};Copy the code

Method 2: Use the postCSS configuration file postcss.config.js and webpack.config.js to write postCSs-loader directly.

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",],},],},};Copy the code

Postcss. Config. Js:

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

Method 3: The browser compatibility can be written in package.json, postcss.config.js only need to load autofixer.

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",],},],},};Copy the code

Postcss. Config. Js:

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

Package. Json:

"browserslist": {
  "last 2 version"."1%" >."ios 7"
}
Copy the code

Resources inline

Some scenarios require resource inlining. Common scenarios include:

  1. Initialization script for the page frame
  2. To reducehttpNetwork request, turn some small pictures intobase64Content into code, less requests
  3. cssInlining increases the page experience
  4. Early executedjs, such asREMplan

htmlinline

In multi-page projects, there are many common tags in the head, such as meta, which can be extracted as a template and referenced to improve maintainability.

Install [email protected]

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

jsinline

In REM, the font size of the HTML tag is required as soon as possible, so the JS section should be loaded and executed as soon as possible.

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

cssinline

For a better experience and to avoid page flickering, you need to inline the packed CSS into the head.

Install the html-inline-CSS-webpack-plugin, which needs to be placed after the 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(),
  ],
};
Copy the code

You need to extract the CSS into separate files first.

The difference between style-laoder and html-inline-CSS-webpack-plugin is as follows:

  • style-loaderInsert styling is a dynamic process that does not exist in packaged source codestyleTag, passed at execution timejsDynamic insertstyleThe label
  • html-inline-css-webpack-plugin: will be built at build timecssInserted into the pagestyleIn the label

Multi-page application

Each page has an entry and an HTMl-webpack-plugin in plugins.

One drawback to this approach is that each entry adds an 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"})]};Copy the code

With the help of glob, the configuration can be generalized.

Install glob with all pages placed under SRC and entry files 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),
};
Copy the code

sourceMapMethod of use

Code published to the build environment is compressed and obfuscated, but after the compression and obfuscation, the code is like reading gobbledegook.

SourceMap provides a mapping between compressed code and source code.

SourceMap is turned on in the development environment and turned off in the online environment, and the Source Map is uploaded to the error monitoring system during troubleshooting online.

source mapThe keyword

  • evalUse:evalPackage module code
  • source mapProduced:mapfile
  • cheap: Does not contain column information
  • inlineWill:.mapAs aDataURIEmbedded, not generated separately.mapfile
  • module: containsloaderthesourcemap

source maptype

devtool For the first time to build Secondary building Suitability for production environment Locatable code
(none) +++ +++ yes The final output code
eval +++ +++ no webpackGenerated code (module by module)
cheap-eval-source-map + ++ no Loader converted code (lines only)
cheap-module-eval-source-map o ++ no Source code (lines only)
eval-source-map + no The source code
cheap-source-map + o yes afterloaderConverted code only sees lines.)
cheap-module-source-map o yes Source code (lines only)
inline-cheap-source-map + no afterloaderTransformed code (lines only)
inline-cheap-module-source-map o no Source code (lines only)
source-map yes The source code
Inline-source-map no The source code
hidden-source- map yes The source code

Extract page common resources

In the project, several basic libraries, such as React and React-DOM, as well as some common code, are used in multiple pages. When packaging, these will be packaged into the final code, which is wasteful and bulky.

Webpack4 has the SplitChunksPlugin plugin built in.

Chunks parameter description:

  • asyncAsynchronously imported libraries decouple (default)
  • initialSynchronously introduce libraries for separation
  • allSeparation of all imported libraries (recommended)

Put filename in chunks in htMl-webpack-plugin by pulling the name of the base library from cacheGroups and importing it automatically:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
        chunks: ["vendors"."commons"."index"].// The packaged HTML will use those chunks})); ] .optimization: {
    splitChunks: {
      chunks: "async".minSize: 30000.// The smallest size, in bytes, of the public package to be removed
      maxSize: 0.// Maximum size
      minChunks: 1.// The number of times the resource was used (on multiple pages), greater than 1, the minimum number of times
      maxAsyncRequests: 5.// Number of concurrent requests
      maxInitialRequests: 3.// The entry file can be divided into up to 3 js files
      automaticNameDelimiter: "~".// The connection when the file is generated
      automaticNameMaxLength: 30.// Automatic automatic naming maximum length
      name: true.// Make the name set in cacheGroups valid
      cacheGroups: {
        // The above parameters take effect when packaging synchronized code
        vendors: {
          test: /[\\/]node_modules[\\/]/.// Check whether the imported library is in the node_modlues directory
          priority: -10.// The greater the value, the higher the priority. Modules are first packaged into high-priority groups
          filename: "vendors.js".// Package all the libraries into a file called vendors. Js
        },
        default: {
          minChunks: 2./ / it has
          priority: -20./ / it has
          reuseExistingChunk: true.// If a module has already been packaged, ignore the module when repackaged},},},},};Copy the code

tree shaking(Tree shaking)

There are multiple methods in a module, and methods that are used are packaged into bundles, while methods that are not used are not.

Tree shaking is packaging only used methods into the bundle, and the rest are erased during the Uglify phase.

Webpack supports modules: false in.babelrc by default. The Production phase is enabled by default.

Must be ES6 syntax, CJS mode is not supported.

If you want to transform ES6 into ES5 and turn tree shaking on, you need to set module: false in. Babelrc. Otherwise, Babel will transform ES6 into CJS by default.

DCE(Deal Code Elimination)

DEC elimination is the elimination of dead code.

  1. The code will not be executed
if (false) {
  console.log("Code will not be executed.");
}
Copy the code
  1. The results of code execution are not used
function a() {
  return "this is a";
}
let A = a();
Copy the code
  1. Code is written, not read
let a = 1;
a = 2;
Copy the code

tree shakingThe principle of

At compile time, it is important to determine whether code is used or not. It is not possible to analyze what code is used or not at runtime. Tree Shaking marks unused code with comments and removes it at uglify.

Using the features of ES6 module:

  • Can only appear as a statement at the top level of a module
  • importCan only be string constants
  • import bindingisimmutable

Delete uselesscss

  • purgecss-webpack-plugin: Walks through the code to identify what is already usedcss class
    • andmini-css-extract-pluginTogether with
  • uncss:htmlNeed to pass throughjsdomLoad, all styles passpostCSSParse, passdocument.querySelectorTo identify thehtmlSelector not found in file
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})}),]};Copy the code

scope hoistingUse and principle analysis

Building enough code results in a large number of closures, as shown in the following figure:

When an external module is introduced, it is wrapped in a function. As more modules are introduced, a large amount of function-wrapped code will be generated, resulting in larger volume. At runtime, the memory overhead will be larger due to the more scoped functions created.

  • bewebpackThe converted module is wrapped in a layer
  • importWill be converted to__webpack_require

Analysis of the

  1. What came out was oneIIFE(Anonymous closure)
  2. modulesIs an array, and each entry is a module initialization function
  3. __webpack_requireUsed to load the module, returnsmodule.exports
  4. throughWEBPACK_REQUIRE_METHOD(0)Start the program

scope hoistingThe principle of

Place all module code in a function scope in reference order, and then rename some variables appropriately to prevent variable name conflicts.

Module calls have sequence, module A calls module B, because there are function packages, the order of module A and module B does not matter. If the package code is eliminated, modules need to be discharged according to the reference order of modules, and module B should be placed before module A, so that module B can be called by module A.

Scope collieries can reduce function declarations and memory overhead. The Production phase is enabled by default.

Must be ES6 syntax, CJS does not support.

Ps: Scope reactorizes multiple scopes into one scope. When a module is referenced more than once, it has no effect. If a module is referenced more than once, the module’s code is inlined multiple times, increasing the size of the packaged file.

useESLint

ESLint can unify the team’s code style and help find bugs.

Two ways to use:

  • withCI/CDintegration
  • withwebpackintegration

webpackwithCI/CDintegration

Husky needs to be installed.

Add scripts and modify files through lint-staged checks.

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

webpackwithESLintintegration

With eslint-Loader, the build is to check the JS specification

Install plugins babel-eslint, eslint, eslint-config-airbnb, eslint-config-airbnb-base, eslint-loader, eslint-plugin-import, eslint-plugin- JSX – ally, eslint – plugin – react.

Create the.eslintrc.js file

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

The webpack.config.js file configures the eslint-loader

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

Code segmentation and dynamicsimport

For large Web applications, putting all of your code in one file is not efficient enough, especially if some of your code blocks are only used in special cases.

Webpack has a feature that breaks your code into chunks and loads them when the code runs and needs them.

Usage Scenarios:

  • Pull the same code into a shared block
  • The script loads lazily, making the initial download smaller

Lazy way to load JS scripts

  • CJS:require.ensure
  • ES6:import(Currently no native support, yesbableTranslation)

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

Babelrc file:

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

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}>Am I</div>
        <img src={img} alt="" />
      </div>); }}Copy the code

One thing to note here is that when cacheGroups is configured and minChunks are set to 1, the lazy loading script set above does not take effect because the import is statically analyzed at load time.

cacheGroups: {
  commons: {
    name: "commons".chunks: "all".priority: -20.// The greater the value, the higher the priority. Modules are first packaged into high-priority groups
    minChunks: 1,}}Copy the code

Multi-process/multi-instance: parallel compression

Method 1: Use the webpack-parallel-ugli-fi -plugin

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,},},}),],};Copy the code

Method 2: Use uglifyjs-webapck-plugin to enable the PARALLEL parameter

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]}Copy the code

Terser-webpack-plugin enables parallel (recommended by Webpack4)

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

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

Increase build speed

React, react-dom, redux, react-Redux base package, and service base package into a file

Method: The DLLPlugin is used for subcontracting, and the DLLReferencePlugin references manifest.json

useDLLPluginIn the subcontract

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",})]};Copy the code

Introduced in webpack. Config. Js

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

With the use of Webpack4 in the project, the dependency on DLLS is not so big, and the improvement of DLLS is not particularly obvious, while the hard-source-webpack-plugin can greatly improve the secondary build. However, in a real front-end factory, DLLS are still necessary. For a team, it’s basically the same technology stack, react or VUE. At this point, it is common practice to bundle the common framework into a common bundle for all projects to use. DLLS are perfect for this scenario: grouping multiple NPM packages into a common package. Therefore, it is still valuable to use DLLS for subcontracting projects within the team.

SplitChunks can also be used to do the DllPlugin, but it is recommended to use splitChunks to extract common JS files between pages, because it takes time to build the base package each time you use splitChunks to extract it. The following basic package time can be omitted.

Improve the speed of secondary construction

Method 1: Use terser-webpack-plugin to enable caching

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

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

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

Narrow your Build goals

For example, babel-loader does not resolve node_modules

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

Reduce file search scope

  • To optimize theresolve.modulesConfiguration (reduce module search hierarchy)
  • To optimize theresolve.mainFieldsconfiguration
    • To find the firstpackage.jsonIn themainThe file specified in the field -> find the root directoryindex.js– > searchlib/index.js
  • To optimize theresolve.extensionsconfiguration
    • Module path lookup,import xx from "index"Will look for the.jsThe suffix
  • The rational use ofalias
module.exports = {
  resolve: {
    alias: {
      react: path.resolve(__dirname, "./node_modules/react/dist/react.min.js"),},modules: [path.resolve(__dirname, "node_modules")].// Find dependencies
    extensions: [".js"].// Find the module path
    mainFields: ["main"].// find the entry file}};Copy the code