Getting started with wepack4 configuration

1. Introduction to webpack

1.1 What is webpack

webpackIt is a front-end 资源构建tool, a 静态模块打包器(module bundler).
From webpack's point of view, all front-end resource files ( js/json/css/img/less/...) will be 模块processed.
It will perform static analysis based on 模块, 依赖关系and package and generate corresponding 静态资源(bundle).

insert image description here

webpack will start from the packaging entry and find all dependencies (js/json/css/img/less/…), webpack will use these dependencies as chunkblocks (code blocks). Then convert the various file contents in the chunk block into content that the browser can understand: less -> css, es6 syntax -> syntax that the browser can understand... These transformations are 打包产物called bundle.

Chunk is a collection of modules in the Webpack packaging process. We know that the packaging of Webpack starts with an entry file, which can also be said to be an entry module. The entry module refers to this other module, and the module then refers to the module. Webpack packages modules one by one through the reference relationship, and these modules form a Chunk.

If we have multiple entry files, multiple packaging paths may be generated, and one path will form a Chunk.

insert image description here

1.2 Five core concepts of webpack

1.2.1 Entry

入口(Entry)Instructs webpack which file to 入口起点start packing and analyzing the build 内部依赖图.

1.2.2 Output

输出(Output)bundles 输出Instructs webpack where and how the bundled resources should go 命名.

1.2.3 Loader

LoaderLet webpack handle those non-JavaScript files (webpack itself only understands JavaScript).
The loader converts resources not recognized by webpack 翻译as resources recognized by webpck.

1.2.4 Plugins

插件(Plugins)Can be used to perform a wider range of tasks. Plugins range from 打包优化and 压缩to redefining the environment 变量.

1.2.5 Mode

模式(Mode)A configuration that instructs webpack to use the corresponding mode

options describe features
development Will set the value of process.env.NODE_ENV in DefinePlugin to development. Enable NamedChunksPlugin and NamedModulesPlugin. An environment that allows code to be debugged and run locally
production Will set the value of process.env.NODE_ENV in DefinePlugin to production. Enables FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin, UglifyJsPlugin and TerserPlugin. It allows the code to optimize the online running environment.

Second, the first experience of webpack

2.1 Initial configuration

  1. Initialize package.json

Enter command:

npm init
  1. Download and install webpack

Enter command:

npm install webpack webpack-cli -g
npm install webpack webpack-cli -D
  • webpack-cli: The webpack command line supports the use of webpack functions by using instructions.
  • -g: global installation
  • -D: local installation

2.2 Compile and package the application

  1. Create a file
// index.js
import data from './data.json';
console.log(data);
function add(x, y) {
    
    
  return x + y;
}
console.log(add(1, 2));
// package.json
{
    
    
  "name": "webpack_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    
    
    "dev": "webpack src/index.js -o build/built.js --mode=development",
    "pro": "webpack src/index.js -o build/built.js --mode=production"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    
    
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11"
  }
}
  1. run command
  • Development environment instructions:
webpack src/index.js -o build/built.js --mode=development

webpack will start packaging ./src/index.jsas the entry file, and output to after packaging ./build/built.js. The overall packaging environment is 开发the environment.

  • Production environment instructions:
webpack src/index.js -o build/built.js --mode=production

webpack will start packaging ./src/index.jsas the entry file, and output to after packaging ./build/built.js. The overall packaging environment is 生产the environment.

  1. in conclusion
  • webpack is able to compile bundled js 和 jsonfiles.
  • A syntax that converts the modular syntax of es6 浏览器能识别.
  • The production environment has one more code than the development environment 压缩js.
  1. question
  • css、imgFiles such as packages cannot be compiled .
  • Could not js 的 es6convert base syntax to es5 以下语法.

3. Basic configuration of webpack development environment

3.1 Create a configuration file

  1. Create a file
  • webpack.config.js: configuration file for webpack
  • Role: Instruct webpack what to do (when you run webpackthe command, 加载the configuration inside will be displayed)
  • All build tools are platform-based and adopt nodejsmodularity .默认commonjs
  1. The configuration is as follows
const {
    
     resolve } = require("path"); // node 内置核心模块,用来处理路径问题。
module.exports = {
    
    
  entry: "./src/index.js", // 入口文件
  output: {
    
    
    // 输出配置
    filename: "built.js", // 输出文件名
    path: resolve(__dirname, "build"), // 输出文件路径配置
  },
  mode: "development", //开发环境
};
  1. Run command:webpack

3.2 Packaging style resources

  1. Create a file
/* index.css */
html, body{
    
    
  margin: 0;
  padding: 0;
  height: 100%;
  background-color: pink;
}
// index.js
// 引入样式资源
import './index.css';
import './index.less';
// index.less
#title {
  color: #fff;
}
  1. Download and install the loader package
npm i css-loader style-loader less-loader less -D
  1. Modify the configuration file
// resolve用来拼接绝对路径的方法
const {
    
     resolve } = require("path");

module.exports = {
    
    
  // webpack配置
  // 入口起点
  entry: "./src/index.js",
  // 输出
  output: {
    
    
    // 输出文件名
    filename: "built.js",
    // 输出路径
    // __dirname nodejs的变量,代表当前文件的目录绝对路径
    path: resolve(__dirname, "build"),
  },
  // loader的配置
  module: {
    
    
    rules: [
      // 详细loader配置
      // 不同文件必须配置不同loader处理
      {
    
    
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader进行处理
        use: [
          // use数组中loader执行顺序:从右到左,从下到上 依次执行
          // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          "style-loader",
          // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
          "css-loader",
        ],
      },
      {
    
    
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          // 将less文件编译成css文件
          // 需要下载 less-loader和less
          "less-loader",
        ],
      },
    ],
  },
  // plugins的配置
  plugins: [
    // 详细plugins的配置
  ],
  // 模式
  mode: "development", // 开发模式
  // mode: 'production'
};

  1. Run command: webpack

3.3 Packaging HTML resources

  1. Create a file
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpack</title>
</head>

<body>
  <h1 id="title">hello html</h1>
</body>

</html>
function add(x, y) {
    
    
  return x + y;
}

console.log(add(2, 3));
  1. Download and install the plugin package
npm install --save-dev html-webpack-plugin
  1. Modify the configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/index.js",
  output: {
    
    
    filename: "built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      // loader的配置
    ],
  },
  plugins: [
    // plugins的配置
    // html-webpack-plugin
    // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
    // 需求:需要有结构的HTML文件
    new HtmlWebpackPlugin({
    
    
      // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
      template: "./src/index.html",
    }),
  ],
  mode: "development",
};

3.4 Pack image resources

  1. Create a file
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpack</title>
</head>
<body>
  <div id="box1"></div>
  <div id="box2"></div>
  <div id="box3"></div>
  <img src="./angular.jpg" alt="angular">
</body>
</html>
import './index.less';
#box1{
  width: 100px;
  height: 100px;
  background-image: url('./vue.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

#box2{
  width: 200px;
  height: 200px;
  background-image: url('./react.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}

#box3{
  width: 300px;
  height: 300px;
  background-image: url('./angular.jpg');
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
  1. Download and install the loader package
npm install --save-dev html-loader url-loader file-loader
  1. Modify the configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/index.js",
  output: {
    
    
    filename: "built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.less$/,
        // 要使用多个loader处理用use
        use: ["style-loader", "css-loader", "less-loader"],
      },
      {
    
    
        // 问题:默认处理不了html中img图片
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 使用一个loader
        // 下载 url-loader file-loader,url-loader 依赖 file-loader
        loader: "url-loader",
        options: {
    
    
          // 图片大小小于8kb,就会被base64处理
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)
          limit: 8 * 1024,
          // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
          // 解析时会出问题:[object Module]
          // 解决:关闭url-loader的es6模块化,使用commonjs解析
          esModule: false,
          // 给图片进行重命名
          // [hash:10]取图片的hash的前10位
          // [ext]取文件原来扩展名
          name: "[hash:10].[ext]",
        },
      },
      {
    
    
        test: /\.html$/,
        // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
        loader: "html-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",
};

3.5 Packaging other resources

  1. scope of use

There is no need to do any processing, just output intact, such as font icons.

  1. configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/index.js",
  output: {
    
    
    filename: "built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
    
    
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: "file-loader",
        options: {
    
    
          name: "[hash:10].[ext]",
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",
};

3.6 devServer

  1. effect

Development server devServer: used for automation (automatically compile, automatically open the browser, automatically refresh the browser), developers only need to focus on coding

  1. configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/index.js",
  output: {
    
    
    filename: "built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      // 打包其他资源(除了html/js/css资源以外的资源)
      {
    
    
        // 排除css/js/html资源
        exclude: /\.(css|js|html|less)$/,
        loader: "file-loader",
        options: {
    
    
          name: "[hash:10].[ext]",
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",

  // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  // 特点:只会在内存中编译打包,不会有任何输出
  // 启动devServer指令为:npx webpack-dev-server
  devServer: {
    
    
    // 项目构建后路径
    contentBase: resolve(__dirname, "build"),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 3000,
    // 自动打开浏览器(默认浏览器)
    open: true,
  },
};

  • npx is a tool for executing Node packages. It has been bundled with npm since version 5.2.
  • By default, first check if the package to be executed exists in the path (ie in the project); if it exists, it will be executed; if it does not exist, it means that the package has not been installed, npx will install its latest version, and then execute it ;
  1. run command
npx webpack-dev-server

3.7 Development environment configuration

  1. Purpose

Development environment configuration: allow the code to run (just run the code)

Run the project command:

  • webpack: will output the packaging result
  • npx webpack-dev-server: only in 内存中编译打包,没有输出
  1. configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      // loader的配置
      {
    
    
        // 处理less资源
        test: /\.less$/,
        use: ["style-loader", "css-loader", "less-loader"],
      },
      {
    
    
        // 处理css资源
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
    
    
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: "url-loader",
        options: {
    
    
          limit: 8 * 1024,
          name: "[hash:10].[ext]",
          // 关闭es6模块化
          esModule: false,
          outputPath: "imgs",
        },
      },
      {
    
    
        // 处理html中img资源
        test: /\.html$/,
        loader: "html-loader",
      },
      {
    
    
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: "file-loader",
        options: {
    
    
          name: "[hash:10].[ext]",
          outputPath: "media",
        },
      },
    ],
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",
  devServer: {
    
    
    contentBase: resolve(__dirname, "build"),
    compress: true,
    port: 3000,
    open: true,
  },
};

  1. run command
npx webpack-dev-server

4. Basic configuration of webpack production environment

Why configure the production environment separately

  • In the development environment, css is placed in js, resulting in larger js files
  • Because the css is placed in the js, the css will not take effect until the js is loaded, which will cause flashing
  • The purpose of the development environment is to make the code run and debug; the purpose of the production environment is to make the code run faster and more smoothly. The production environment will use code compression, css compatibility guarantee and other means
  • Compression, compatibility, etc. take a long time, and putting it in the development environment will make the build slower, which is not conducive to development and debugging

4.1 Extract css into a separate file

  1. download plugin
npm install --save-dev mini-css-extract-plugin
  1. Modify the configuration file
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: [
          // 创建style标签,将样式放入
          // 'style-loader', 

          // 这个loader取代style-loader。作用:提取js中的css成单独文件
          MiniCssExtractPlugin.loader,
          // 将css文件整合到js文件中
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
    
    
      // 对输出的css文件进行重命名,默认名称是main.css
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};
  • style-loader: Create a style tag and put the style in
  • MiniCssExtractPlugin.loader: Extract the css in js into a separate file

4.2 css compatibility processing

  1. download loader
npm install --save-dev postcss-loader postcss-preset-env
  1. Modify the configuration file
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          /*
            css兼容性处理:postcss --> postcss-loader postcss-preset-env

            帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式

            "browserslist": {
              // 开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
              "development": [
                "last 1 chrome version",
                "last 1 firefox version",
                "last 1 safari version"
              ],
              // 生产环境:默认是看生产环境
              "production": [
                ">0.2%",
                "not dead",
                "not op_mini all"
              ]
            }
          */
          // 使用loader的默认配置
          // 'postcss-loader',
          // 修改loader的配置
          {
    
    
            loader: 'postcss-loader',
            options: {
    
    
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
    
    
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

  1. Modify package.json
{
    
    
  "browserslist": {
    
    
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  }
}

4.3 compress css

  1. Download the installation package
npm install --save-dev optimize-css-assets-webpack-plugin
  1. Modify the configuration file
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 设置nodejs环境变量
// process.env.NODE_ENV = 'development';

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
    
    
            loader: 'postcss-loader',
            options: {
    
    
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
    
    
      filename: 'css/built.css'
    }),
    // 压缩css
    new OptimizeCssAssetsWebpackPlugin()
  ],
  // optimization: {
    
    
  //   minimize: true,
  //   minimizer: [
  //     // 压缩 js
  //     new TerserWebpackPlugin(),
  //     // 压缩css 
  //     new OptimizeCssAssetsWebpackPlugin(),
  //   ],
  // },
  mode: 'development'
};
  • When the official webpack documentation introduces, OptimizeCssAssetsWebpackPluginthe plug-ins are not configured in pluginsan array. Instead, configure them in optimization.minimizerthe array.
  • The reason is: configured in plugins, webpack will use this plugin at startup. And configured in optimization.minimizer, it will only be optimization.minimizeused when this feature is enabled. Therefore, webpack recommends that plug-ins like compression should be configured in optimization.minimizeran array. to facilitate optimization.minimizeunified control. (The production environment will enable minimize by default)
  • It should be noted that if configured minimizer, it means that the developer is 自定义compressing the plug-in. The internal JS compressor will be overwritten. Therefore, js compression needs to be manually configured.

4.4 js syntax check

  1. Download the installation package
npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
  1. Modify the configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      /*
        语法检查: eslint-loader 依赖 eslint 库
          注意:只检查自己写的源代码,第三方的库是不用检查的
          设置检查规则:
            package.json中eslintConfig中设置~
              "eslintConfig": {
                // 继承 airbnb-base 的规则检查
                "extends": "airbnb-base"
              }
            airbnb(检查规则) --> eslint-config-airbnb-base  eslint-plugin-import eslint
      */
      {
    
    
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
    
    
          // 自动修复eslint的错误
          fix: true,
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",
};

  1. Configure package.json

Configure inspection rules

{
    
    
  "eslintConfig": {
    
    
    "extends": "airbnb-base",
    "env": {
    
    
      // eslint不认识 window、navigator全局变量,支持浏览器端全局变量
      "browser": true
    }
  }
}

4.5 js compatibility processing & es6 syntax conversion

  1. effect

Convert the grammar of es6 to the grammar of es5

  1. Download the installation package
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
  1. Modify the configuration file
const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      /*
        js兼容性处理:babel-loader @babel/core (babel的核心库)
          1. 基本js兼容性处理 --> @babel/preset-env
            问题:只能转换基本语法,如promise高级语法不能转换
          2. 全部js兼容性处理 --> @babel/polyfill  
            问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
            使用方法:
              引入即可,在index.js中 import '@babel/polyfill ';
          3. 需要做兼容性处理的就做:按需加载  --> core-js
      */
      {
    
    
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
    
    
          // 预设:指示babel做怎么样的兼容性处理
          presets: [
            [
              "@babel/preset-env",
              {
    
    
                // 按需加载
                useBuiltIns: "usage",
                // 指定core-js版本
                corejs: {
    
    
                  version: 3,
                },
                // 指定兼容性做到哪个版本浏览器
                targets: {
    
    
                  chrome: "60",
                  firefox: "60",
                  ie: "9",
                  safari: "10",
                  edge: "17",
                },
              },
            ],
          ],
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
    }),
  ],
  mode: "development",
};

4.6 js compression

  1. The js code will be automatically compressed in the production environment
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    })
  ],
  // 生产环境下会自动压缩js代码
  mode: 'production'
};

In the production environment, the UglifyJsPlugin plug-in will be enabled to compress js.

  1. Manually configuring
    webpack believes that if the minimizer is configured, it means that the developer is customizing to compress the plugin. The internal JS compressor will be overwritten.

The configuration needs to be modified as follows:

const {
    
     resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  optimization: {
    
    
    minimize: true,
    minimizer: [
      // 压缩 js
      new TerserWebpackPlugin(),
      // 压缩css
      new OptimizeCssAssetsWebpackPlugin(),
    ],
  },
  mode: "development",
};

4.7 HTML Compression

  1. Modify the configuration file
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html',
      // 压缩html代码
      minify: {
    
    
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

4.8 Production environment configuration

  1. Modify the configuration file
const {
    
     resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
    
    
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: [...commonCssLoader]
      },
      {
    
    
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
      },
      /*
        正常来讲,一个文件只能被一个loader处理。
        当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
          先执行eslint 再执行babel
      */
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
    
    
          fix: true
        }
      },
      {
    
    
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
    
    
          presets: [
            [
              '@babel/preset-env',
              {
    
    
                useBuiltIns: 'usage',
                corejs: {
    
    version: 3},
                targets: {
    
    
                  chrome: '60',
                  firefox: '50'
                }
              }
            ]
          ]
        }
      },
      {
    
    
        test: /\.(jpg|png|gif)/,
        loader: 'url-loader',
        options: {
    
    
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
          esModule: false
        }
      },
      {
    
    
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
    
    
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
    
    
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html',
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

Five, webpack optimization configuration

5.1 Overview of performance optimization

  1. Performance Optimization Classification
  • Development environment performance optimization
  • Production environment performance optimization
  1. Development environment performance optimization
  • Optimize package build speed
    • HMR
  • Optimize code debugging
    • source-map
  1. Production environment performance optimization
  • Optimize package build speed
    • oneOf
    • babel-cache
    • Multi-process packaging
    • externals
    • dll
  • Optimize code performance
    • Cache (hash-chunkhash-contenthash)
    • tree shaking
    • code split
    • lazy loading/preloading
    • pwa

5.2 HMR

  1. introduce
  • HMR: hot module replacementHot Module Replacement / Hot Module Replacement
  • Function: If a module changes, it will repackage this 一个module (and 不是打包所有模块) greatly improve the construction speed
  • Style file: You can use the HMR function: because style-loaderit is implemented internally
  • js file: 不能use the HMR function by default: 修改js code is required, add the code that supports the HMR function
    • 非入口js文件Note: The HMR function can only process other files for js processing .
  • html file: 不能The HMR function is used by default, and it will cause problems: the html file cannot be hot updated (the html code written locally does not update the browser) (the HMR function is not required)
    • Solution: Modify the entry entry and import the html fileentry: ['./src/js/index.js', './src/index.html']
  1. js file to enable the HMR function
// index.js
// 引入
import print from './print';
import '../css/iconfont.css';
import '../css/index.less';

console.log('index.js文件被加载了~');

print();

function add(x, y) {
    
    
  return x + y;
}

console.log(add(1, 3));

if (module.hot) {
    
    
  // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
  module.hot.accept('./print.js', function() {
    
    
    // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
    // 会执行后面的回调函数
    print();
  });
}
// print.js
console.log('print.js被加载了~');

function print() {
    
    
  const content = 'hello print';
  console.log(content);
}

export default print;
  1. configuration file
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      // loader的配置
      {
    
    
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
    
    
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
    
    
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
    
    
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
    
    
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
    
    
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
    
    
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    
    
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    // 开启HMR功能
    // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
    hot: true
  }
};

5.3 source-map

  1. Introduction

source-map: A way to provide source code to the post-build code 映射技术(if there is an error in the post-build code, 映射it can be 追踪源代码wrong by passing)

  1. category

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

  • source-map: external
    • Exact information about the error code and the location of the error in the source code
  • inline-source-map: inline
    • Only generate an inline source-map
    • Exact information about the error code and the location of the error in the source code
  • hidden-source-map: external
    • error code error reason, but no error location
    • Can't track source code errors, only prompts to the wrong location of the code after construction
    • Can hide source code, not build code
  • eval-source-map: inline
    • Each file generates a corresponding source-map, all in eval
    • Exact information about the error code and the location of the error in the source code
  • nosources-source-map: external
    • Accurate error code information, but no source code information
    • Can hide source code, also hide build code
  • cheap-source-map: external
    • Exact information about the error code and the location of the error in the source code
    • only accurate to the line
  • cheap-module-source-map: external
    • Exact information about the error code and the location of the error in the source code
    • The module will add the source map of the loader
  1. The difference between inline and external
  • The file is generated externally, but not inline (inline directly puts the source-map information in js)
  • Inline builds are faster
  1. source-map selection
  • Development environment: fast speed and more friendly debugging
    • 速度快(eval>inline>cheap>…) eval-cheap-souce-map > eval-source-map
    • Debugging is more friendly: source-map > cheap-module-souce-map > cheap-souce-map
    • Suggestions: eval-source-map (vue, react scaffolding) / eval-cheap-module-souce-map
  • Production environment: Should the source code be hidden? Should debugging be more friendly?
    • Inlining will increase the size of the code, so do not inline in the production environment
    • nosources-source-map hide all
    • hidden-source-map only hides the source code, and will prompt the code error message after the build
    • source-map (debugging friendly) / cheap-module-source-map (faster)
  1. configuration information
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    
    
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      // loader的配置
      {
    
    
        // 处理less资源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
    
    
        // 处理css资源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
    
    
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
    
    
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 关闭es6模块化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
    
    
        // 处理html中img资源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
    
    
        // 处理其他资源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
    
    
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    
    
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true
  },
  devtool: 'eval-source-map'
};

5.4 oneOf

  1. Solve the problem
  • Without using oneOf, all rules will be matched once, some can be hit, and some cannot be hit.
  • When using oneOf, the loader in oneOf will only match one
  • Optimize the packaging and building speed of the production environment
  1. configuration file
const {
    
     resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = "production";

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: "postcss-loader",
    options: {
    
    
      ident: "postcss",
      plugins: () => [require("postcss-preset-env")()],
    },
  },
];

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: "pre",
        loader: "eslint-loader",
        options: {
    
    
          fix: true,
        },
      },
      {
    
    
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
    
    
            test: /\.css$/,
            use: [...commonCssLoader],
          },
          {
    
    
            test: /\.less$/,
            use: [...commonCssLoader, "less-loader"],
          },
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            loader: "babel-loader",
            options: {
    
    
              presets: [
                [
                  "@babel/preset-env",
                  {
    
    
                    useBuiltIns: "usage",
                    corejs: {
    
     version: 3 },
                    targets: {
    
    
                      chrome: "60",
                      firefox: "50",
                    },
                  },
                ],
              ],
            },
          },
          {
    
    
            test: /\.(jpg|png|gif)/,
            loader: "url-loader",
            options: {
    
    
              limit: 8 * 1024,
              name: "[hash:10].[ext]",
              outputPath: "imgs",
              esModule: false,
            },
          },
          {
    
    
            test: /\.html$/,
            loader: "html-loader",
          },
          {
    
    
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: "file-loader",
            options: {
    
    
              outputPath: "media",
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: "css/built.css",
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
  ],
  mode: "production",
};

5.5 Caching

  1. cache classification
  • Babel cache (make the second packaging build faster)
    • cacheDirectory: true
  • File resource cache (let the code run online and run the cache better)
    • Usage scenario: The server is configured with cache, and users will use the cache file. At this time, a bugfix version is released, and the changed file will be replaced. How to make the user abandon the cache and use a new file
    • File Resource Cache Method
      • hash: A unique hash value is generated each time wepack builds.
        • Problem: Because js and css use a hash value at the same time. If repackaged, all caches will be invalidated. (Maybe I only changed one file)
      • chunkhash: The hash value generated according to the chunk. If the packaging comes from the same chunk, the hash value will be the same
        • Problem: The hash values ​​of js and css are still the same because css is introduced in js, so they belong to the same chunk
      • contenthash: Generate a hash value based on the content of the file. The hash values ​​of different files must be different
  1. configuration file
const {
    
     resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = "production";

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: "postcss-loader",
    options: {
    
    
      ident: "postcss",
      plugins: () => [require("postcss-preset-env")()],
    },
  },
];

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.[contenthash:10].js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: "pre",
        loader: "eslint-loader",
        options: {
    
    
          fix: true,
        },
      },
      {
    
    
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
    
    
            test: /\.css$/,
            use: [...commonCssLoader],
          },
          {
    
    
            test: /\.less$/,
            use: [...commonCssLoader, "less-loader"],
          },
          /*
            正常来讲,一个文件只能被一个loader处理。
            当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
              先执行eslint 在执行babel
          */
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            loader: "babel-loader",
            options: {
    
    
              presets: [
                [
                  "@babel/preset-env",
                  {
    
    
                    useBuiltIns: "usage",
                    corejs: {
    
     version: 3 },
                    targets: {
    
    
                      chrome: "60",
                      firefox: "50",
                    },
                  },
                ],
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true,
            },
          },
          {
    
    
            test: /\.(jpg|png|gif)/,
            loader: "url-loader",
            options: {
    
    
              limit: 8 * 1024,
              name: "[hash:10].[ext]",
              outputPath: "imgs",
              esModule: false,
            },
          },
          {
    
    
            test: /\.html$/,
            loader: "html-loader",
          },
          {
    
    
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: "file-loader",
            options: {
    
    
              outputPath: "media",
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: "css/built.[contenthash:10].css",
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
  ],
  mode: "production",
  devtool: "source-map",
};

5.6 tree shaking&tree shaking

  1. Purpose

tree shaking: remove useless code and reduce code size

  1. premise
  • Must use ES6 modularity
  • Open the production environment
  1. question

May kill css, @babel/polyfill(side effect) files

Configure in package.json:

  • "sideEffects": false All codes have no side effects (all can do tree shaking)
  • "sideEffects": [" .css", " .less"] Do not perform tree shaking
  1. configuration file
const {
    
     resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = "production";

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: "postcss-loader",
    options: {
    
    
      ident: "postcss",
      plugins: () => [require("postcss-preset-env")()],
    },
  },
];

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.[contenthash:10].js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: "pre",
        loader: "eslint-loader",
        options: {
    
    
          fix: true,
        },
      },
      {
    
    
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
    
    
            test: /\.css$/,
            use: [...commonCssLoader],
          },
          {
    
    
            test: /\.less$/,
            use: [...commonCssLoader, "less-loader"],
          },
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            loader: "babel-loader",
            options: {
    
    
              presets: [
                [
                  "@babel/preset-env",
                  {
    
    
                    useBuiltIns: "usage",
                    corejs: {
    
     version: 3 },
                    targets: {
    
    
                      chrome: "60",
                      firefox: "50",
                    },
                  },
                ],
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true,
            },
          },
          {
    
    
            test: /\.(jpg|png|gif)/,
            loader: "url-loader",
            options: {
    
    
              limit: 8 * 1024,
              name: "[hash:10].[ext]",
              outputPath: "imgs",
              esModule: false,
            },
          },
          {
    
    
            test: /\.html$/,
            loader: "html-loader",
          },
          {
    
    
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: "file-loader",
            options: {
    
    
              outputPath: "media",
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: "css/built.[contenthash:10].css",
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
  ],
  mode: "production",
  devtool: "source-map",
};

5.7 code split

  1. Purpose
  • Code segmentation, divide a large code into multiple codes, load them in parallel, and improve the loading speed
  • Implement on-demand loading
  1. Method 1: Multi-entry

Split files by multiple entries

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

module.exports = {
    
    
  // 单页面应用 单入口最终输出一个bundle
  // entry: './src/js/index.js',
  entry: {
    
    
    // 多页面应用 多入口:每个入口输出一个bundle
    index: "./src/js/index.js",
    test: "./src/js/test.js",
  },
  output: {
    
    
    // [name]:取文件名
    filename: "js/[name].[contenthash:10].js",
    path: resolve(__dirname, "build"),
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
  ],
  mode: "production",
};

  1. Method 2: splitChunks
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    
    
  // 单入口
  // entry: './src/js/index.js',
  entry: {
    
    
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    
    
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html',
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  /*
    1. 可以将node_modules中代码单独打包一个chunk最终输出
    2. 自动分析多入口chunk中,有没有公共的文件(如果公共文件太小,也不会单独生成chunk)。如果有会打包成单独一个chunk
  */
  optimization: {
    
    
    splitChunks: {
    
    
      chunks: 'all'
    }
  },
  mode: 'production'
};
  1. Method 3: Through js code, let a file be packaged into a chunk separately
  • importDynamic import syntax: a file can be packaged separately
  • webpackChunkName: Configure the name of the packaged file, if not configured, it is 0, 1, 2, 3...
function sum(...args) {
    
    
  return args.reduce((p, c) => p + c, 0);
}

import(/* webpackChunkName: 'test' */ "./test")
  .then(({
     
      mul, count }) => {
    
    
    // 文件加载成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    
    
    // eslint-disable-next-line
    console.log("文件加载失败~");
  });

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

5.8 lazy loading & lazy loading

console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
    
    
  // 懒加载~:当文件需要使用时才加载~
  // 预加载 prefetch:会在使用之前,提前加载js文件 
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({
     
      mul }) => {
    
    
    console.log(mul(4, 5));
  });
};

Preloading should be used with caution, because compatibility issues are relatively large.

5.9 pwa & progressive web development app (accessible offline)

  1. status quo

Due to compatibility issues, it is not widely used, and some major manufacturers are using it (Taobao)

  1. Download the installation package

Use workbox (Google open source plugin), webpack needs to install workbox-webpack-plugin

npm install --save-dev workbox-webpack-plugin
  1. configuration file
const {
    
     resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: 'postcss-loader',
    options: {
    
    
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
    
    
          fix: true
        }
      },
      {
    
    
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
    
    
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
    
    
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
    
    
              presets: [
                [
                  '@babel/preset-env',
                  {
    
    
                    useBuiltIns: 'usage',
                    corejs: {
    
     version: 3 },
                    targets: {
    
    
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 开启babel缓存
              // 第二次构建时,会读取之前的缓存
              cacheDirectory: true
            }
          },
          {
    
    
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
    
    
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
    
    
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
    
    
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
    
    
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html',
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new WorkboxWebpackPlugin.GenerateSW({
    
    
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker

        生成一个 serviceworker 配置文件(service-worker.js)~
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};
import {
    
     mul } from "./test";
import "../css/index.css";

function sum(...args) {
    
    
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

/*
  1. eslint不认识 window、navigator全局变量
    解决:需要修改package.json中eslintConfig配置
      "env": {
        "browser": true // 支持浏览器端全局变量
      }
   2. sw代码必须运行在服务器上
      方式一:直接使用 nodejs
      方式二:使用 serve 包
        npm i serve -g
        serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
*/
// 注册serviceWorker
// 处理兼容性问题
if ("serviceWorker" in navigator) {
    
    
  window.addEventListener("load", () => {
    
    
    navigator.serviceWorker
      .register("/service-worker.js")
      .then(() => {
    
    
        console.log("sw注册成功了~");
      })
      .catch(() => {
    
    
        console.log("sw注册失败了~");
      });
  });
}

5.10 Multi-process packaging

  1. Download the installation package
npm install --save-dev thread-loade
  1. configuration file
const {
    
     resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WorkboxWebpackPlugin = require("workbox-webpack-plugin");

// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = "production";

// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
    
    
    // 还需要在package.json中定义browserslist
    loader: "postcss-loader",
    options: {
    
    
      ident: "postcss",
      plugins: () => [require("postcss-preset-env")()],
    },
  },
];

module.exports = {
    
    
  entry: "./src/js/index.js",
  output: {
    
    
    filename: "js/built.[contenthash:10].js",
    path: resolve(__dirname, "build"),
  },
  module: {
    
    
    rules: [
      {
    
    
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: "pre",
        loader: "eslint-loader",
        options: {
    
    
          fix: true,
        },
      },
      {
    
    
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
        oneOf: [
          {
    
    
            test: /\.css$/,
            use: [...commonCssLoader],
          },
          {
    
    
            test: /\.less$/,
            use: [...commonCssLoader, "less-loader"],
          },
          {
    
    
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /* 
                放在某一个loader的后面,会对该loader开启多进程打包。 
                进程启动大概为600ms,进程通信也有开销。
                只有工作消耗时间比较长,才需要多进程打包
              */
              {
    
    
                loader: "thread-loader",
                options: {
    
    
                  workers: 2, // 进程2个
                },
              },
              {
    
    
                loader: "babel-loader",
                options: {
    
    
                  presets: [
                    [
                      "@babel/preset-env",
                      {
    
    
                        useBuiltIns: "usage",
                        corejs: {
    
     version: 3 },
                        targets: {
    
    
                          chrome: "60",
                          firefox: "50",
                        },
                      },
                    ],
                  ],
                  // 开启babel缓存
                  // 第二次构建时,会读取之前的缓存
                  cacheDirectory: true,
                },
              },
            ],
          },
          {
    
    
            test: /\.(jpg|png|gif)/,
            loader: "url-loader",
            options: {
    
    
              limit: 8 * 1024,
              name: "[hash:10].[ext]",
              outputPath: "imgs",
              esModule: false,
            },
          },
          {
    
    
            test: /\.html$/,
            loader: "html-loader",
          },
          {
    
    
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: "file-loader",
            options: {
    
    
              outputPath: "media",
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
    
    
      filename: "css/built.[contenthash:10].css",
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
    
    
      template: "./src/index.html",
      minify: {
    
    
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new WorkboxWebpackPlugin.GenerateSW({
    
    
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker

        生成一个 serviceworker 配置文件~
      */
      clientsClaim: true,
      skipWaiting: true,
    }),
  ],
  mode: "production",
  devtool: "source-map",
};

5.11 externals

  1. effect

Prevent certain packages from being exported to certain dependencies, such as jq, we want to get it from the CDN link, and don't want to package it in.

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

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webpack</title>
</head>

<body>
  <h1 id="title">hello html</h1>
  <!-- 需要手动引入进来 -->
  <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
</body>

</html>

5.12 dll & dynamic link library

  1. Purpose

Use dll technology to package some libraries (third-party libraries: jquery, react, vue...) separately, this operation is pre-operation. When the real business is packaged, there is no need to package such files. You only need to package the business code and import the pre-packaged third-party library.

  1. configuration file
// webpack.dll.js
/*
  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
    当你运行 webpack 时,默认查找 webpack.config.js 配置文件
    需求:需要运行 webpack.dll.js 文件
      --> webpack --config webpack.dll.js
*/

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

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

// webpack.config.js
const {
    
     resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

module.exports = {
    
    
  entry: './src/index.js',
  output: {
    
    
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
    
    
      template: './src/index.html'
    }),
    // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
    new webpack.DllReferencePlugin({
    
    
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
    
    
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],
  mode: 'production'
};

Six, webpack configuration details

6.1 entry

entry point

  1. string
entry: './src/index.js'
  • single entry
  • packaged form一个chunk
  • output 一个bundlefile
  • The default name at this time chunkismain
  1. array
entry: ['./src/index.js', './src/add.js']
  • multi-entry
  • All entry files will eventually only be formed 一个chunk, and only files will be output 一个bundle.
  • The default name at this time chunkismain
  • Only make the html hot update take effect in the HMR function
  • Array files are generally not interdependent, but they are packaged together for some reason
  1. object
  entry: {
    
    
    index: "./src/index.js",
    add: "./src/add.js"
  }
  • multi-entry
  • If there are several entry files, several will be formed chunk, and several bundlefiles will be output
  • The name at this time chunkiskey
  1. special usage
entry: {
    
    
    // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
    index: ["./src/index.js", "./src/count.js"],
    // 形成一个chunk,输出一个bundle文件。
    add: "./src/add.js"
  }

6.2 output

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

module.exports = {
    
    
  entry: "./src/index.js",
  output: {
    
    
    // 文件名称(指定名称+目录)
    filename: "js/[name].js",
    // 输出文件目录(将来所有资源输出的公共目录)
    path: resolve(__dirname, "build"),
    // 所有资源引入公共路径前缀,例如:'imgs/a.jpg' --> '/imgs/a.jpg'
    publicPath: "/",
    chunkFilename: "js/[name]_chunk.js", // 非入口chunk的名称
    // library: '[name]', // 整个库向外暴露的变量名
    // libraryTarget: 'window' // 变量名添加到哪个上 browser
    // libraryTarget: 'global' // 变量名添加到哪个上 node
    // libraryTarget: 'commonjs'
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: "development",
};

6.3 module

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

module.exports = {
    
    
  entry: './src/index.js',
  output: {
    
    
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      // loader的配置
      {
    
    
        test: /\.css$/,
        // 多个loader用use
        use: ['style-loader', 'css-loader']
      },
      {
    
    
        test: /\.js$/,
        // 排除node_modules下的js文件
        exclude: /node_modules/,
        // 只检查 src 下的js文件
        include: resolve(__dirname, 'src'),
        // 优先执行
        enforce: 'pre',
        // 延后执行
        // enforce: 'post',
        // 单个loader用loader
        loader: 'eslint-loader',
        options: {
    
    }
      },
      {
    
    
        // 以下配置只会生效一个
        oneOf: []
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

6.4 resolve

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

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  // 解析模块的规则
  resolve: {
    
    
    // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
    alias: {
    
    
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路径的后缀名
    extensions: ['.js', '.json', '.jsx', '.css'],
    // 告诉 webpack 解析模块是去找哪个目录
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  }
};

6.5 devServer

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

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  resolve: {
    
    
    alias: {
    
    
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  devServer: {
    
    
    // 运行代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
    watchContentBase: true,
    watchOptions: {
    
    
      // 忽略文件
      ignored: /node_modules/
    },
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 自动打开浏览器
    open: true,
    // 开启HMR功能
    hot: true,
    // 不要显示启动服务器日志信息
    clientLogLevel: 'none',
    // 除了一些基本启动信息以外,其他内容都不要显示
    quiet: true,
    // 如果出错了,不要全屏提示~
    overlay: false,
    // 服务器代理 --> 解决开发环境跨域问题
    proxy: {
    
    
      // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
      '/api': {
    
    
        target: 'http://localhost:3000',
        // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
        pathRewrite: {
    
    
          '^/api': ''
        }
      }
    }
  }
};

6.6 optimization

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

module.exports = {
    
    
  entry: './src/js/index.js',
  output: {
    
    
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build'),
    chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
  },
  module: {
    
    
    rules: [
      {
    
    
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'production',
  resolve: {
    
    
    alias: {
    
    
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  optimization: {
    
    
    splitChunks: {
    
    
      chunks: 'all'
      // 默认值,可以不写~
      /* minSize: 30 * 1024, // 分割的chunk最小为30kb
      maxSiza: 0, // 最大没有限制
      minChunks: 1, // 要提取的chunk最少被引用1次
      maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 3, // 入口js文件最大并行请求数量
      automaticNameDelimiter: '~', // 名称连接符
      name: true, // 可以使用命名规则
      cacheGroups: {
        // 分割chunk的组
        // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
        // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 优先级
          priority: -10
        },
        default: {
          // 要提取的chunk最少被引用2次
          minChunks: 2,
          // 优先级
          priority: -20,
          // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
          reuseExistingChunk: true
        } 
      }*/
    },
    // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
    // 解决:修改a文件导致b文件的contenthash变化,在b文件中引入a文件,打包后b文件通过引用a文件的contenthash来引入a文件,修改a文件,a文件的contenthash变化自然会导致b文件发生变化
    runtimeChunk: {
    
    
      name: entrypoint => `runtime-${
      
      entrypoint.name}`
    },
    minimizer: [
      // 配置生产环境的压缩方案:js和css
      new TerserWebpackPlugin({
    
    
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  }
};

reference

Appendix 1 package.json

{
    
    
  "name": "webpack_code",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    
    
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    
    
    "@babel/core": "^7.8.4",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "babel": "^6.23.0",
    "babel-loader": "^8.0.6",
    "core-js": "^3.6.4",
    "css-loader": "^3.4.2",
    "eslint": "^6.8.0",
    "eslint-config-airbnb-base": "^14.0.0",
    "eslint-loader": "^3.0.3",
    "eslint-plugin-import": "^2.20.1",
    "file-loader": "^5.0.2",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.11.1",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "style-loader": "^1.1.3",
    "terser-webpack-plugin": "^2.3.5",
    "thread-loader": "^2.1.3",
    "url-loader": "^3.0.0",
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3",
    "workbox-webpack-plugin": "^5.0.0"
  },
  "dependencies": {
    
    
    "jquery": "^3.4.1"
  },
  "browserslist": {
    
    
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    
    
    "extends": "airbnb-base",
    "env": {
    
    
      "browser": true
    }
  },
  "sideEffects": [
    "*.css"
  ]
}

Guess you like

Origin blog.csdn.net/letianxf/article/details/129227015