Writing in the front

This blog is a study note taken during the course of the Webpack course taught by Teacher Coderwhy. There may be some mistakes in memory, I hope you can point out. Write out the main purpose is to facilitate their own review, if it can help others that is even better.

Webpack is a static module bunndler for modern JavaScript applications.—-webpack is a static modular packaging tool for modern JavaScript applications.

01. Webpack early experience

Webpack -cli NPM install webpack webpack-cli -g to complete the global installation. Then you can create an empty project to get started. The project structure is as follows:

├─ ├─ SRC │ ├─ ├─ ├─ ├─ ├─ ├─ ├─ download.jsonCopy the code

After the project is created, first install the NPM install webpack webpack-cli -d, then the console will execute the command webpack will generate directory dist, and then manually import the code in dist into index.html, you can start to experience Webpack. Note that if the SRC entry file name must be named index.js, webPack default will match this file from the folder. If you change the file name to main.js, you should specify the entry file webpack –entry./ SRC /main.js –output-path./dist. You can also configure the scripts section of the package.json file to add a new script.

  "scripts": {
    "build": "webpack" // Run NPM run build
  }
Copy the code

This completes your first experience with WebPack.

02. Webpack configuration file -CSS

The project structure

├ ─ dist ├ ─ SRC │ ├ ─ CSS │ │ └ ─ file. (CSS | less) │ ├ ─ js │ │ └ ─ file. The js │ └ ─ main. Js ├ ─. Browserslistrc// Compatible browser version dependent (also configurable in package.json)├ ─ postcss. Config. Js// CSS compatibility related├ ─ wk. A config. Js/ / webpack configuration├ ─ index. The HTML ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Browser compatibility can be viewed in caniuse: Portals can also be viewed by using the command NPX browserslist “compatibility information”, eg: NPX browserslist “>1%, last 2 version, not dead”.

Less: Use the less tool to convert the xxx.less file to the xxx. CSS file. Install less: NPM install less and run lessc xxx.less xxx. CSS

Because different browsers have different compatibility with the CSS, they use prefixes of the CSS. You can run the following command: NPX postcss –use autoprefixer -o output.css source.css

The next step is to write the webPack configuration file:

const path = require("path");
module.exports = {
  entry: "./src/main.js".output: {
    filename: "bundle.js".// File name generated after packaging
    path: path.resolve(__dirname, "./build"), // The absolute path to the generated file
  },
  module: {
    rules: [{// Rules use regular expressions
        test: /\.css$/.// Match resources
        use: [
          // Write order (bottom up, right to work, back to front)
          "style-loader".// {loader: "style-loader"
          {
            loader: "css-loader".options: {
              importLoaders: 1.// The new CSS file is loaded, go back to the previous loader again}},"postcss-loader",]}, {test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 2,}},"postcss-loader"."less-loader",],},],},};// package.json script configuration
  "scripts": {
    "build": "webpack --config wk.config.js"
  },
Copy the code

03. Webpack handles other resources — images & fonts

The project structure

├ ─ dist ├ ─ SRC │ ├ ─ CSS │ │ └ ─ file. (CSS | less) │ ├ ─ img │ │ └ ─ file. (PNG | JPG) │ ├ ─ js │ │ └ ─ file. The js │ └ ─ main. Js ├ ─ browserslistrc// Compatible browser version dependent (also configurable in package.json)├ ─ postcss. Config. Js// CSS compatibility related├ ─ wk. A config. Js/ / webpack configuration├ ─ index. The HTML ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Webpack configuration file:

const path = require("path");
module.exports = {
  entry: "./src/main.js".output: {
    filename: "bundle.js".// Must be an absolute path
    path: path.resolve(__dirname, "./build"),},module: {
    rules: [{test: /\.css$/,
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 1,}},"postcss-loader",]}, {test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 2,}},"postcss-loader"."less-loader",]}, {test: /\.(png|jpe? g|gif|svg)$/,
        use: [
          {
            loader: "url-loader".options: {
              limit: 1024 * 1024.// Images smaller than 1M are directly converted to Base64 to reduce HTTP requests
              name: "img/[name].[hash:6].[ext]",},},],},},};// package.json script configuration
  "scripts": {
    "build": "webpack --config wk.config.js"
  }
Copy the code

Add the file name rule:

  • [ext]: Extension of the processing file
  • [name]: indicates the processing file name
  • [hash]: handles the MD4 value of the file content
  • [contentHash]: the result of file-loader is consistent with that of [hash]
  • [hash:< length >]: Intercepts the hash value length
  • [ext]: Path of the file relative to the WebPack configuration file

Asset Module Type

  • Before Webpack5, loaders such as raw-loader, url-loader, and file-loader are required to load resources

  • Once Webpackage 5 starts, you can use asset Module Type directly in place of the loader above

    • Asset/Resource: emits a separate file and exports the URL (corresponding to file-loader)

    • Asset /inline: Export a resource’s data URL (corresponding to url-loader)

    • Asset /source: Export source code for the resource (corresponding to row-loader)

    • Asset: Automatically selects between exporting a data URL and sending a separate file. Url-loader was used previously, and the resource volume limit was configured.

Wk.config. js supports the directory structure after loading font resources

The project structure

├ ─ dist ├ ─ SRC │ ├ ─ CSS │ │ └ ─ file. (CSS | less) │ ├ ─ the font │ │ └ ─ file. (CSS | eot | TFF | woff | woff2}) │ ├ ─ img │ │ └ ─ file. │ (PNG | JPG) ├ ─ js │ │ └ ─ file. Js │ └ ─ main. Js ├ ─. Browserslistrc// Compatible browser version dependent (also configurable in package.json)├ ─ postcss. Config. Js// CSS compatibility related├ ─ wk. A config. Js/ / webpack configuration├ ─ index. The HTML ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

With asset Module Type, the wk.config.js configuration is updated to:

const path = require("path");

module.exports = {
  entry: "./src/main.js".output: {
    filename: "bundle.js".// Must be an absolute path
    path: path.resolve(__dirname, "./build"),
    // assetModuleFilename: "img/[name].[hash:6][ext]"
  },
  module: {
    rules: [{// Rules use regular expressions
        test: /\.css$/.// Match resources
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 1,}},"postcss-loader"] {},test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 2,}},"postcss-loader"."less-loader",]}, {test: /\.(png|jpe? g|gif|svg)$/.// type: "asset/resource", file-loader effect
        // type: "asset/inline", url-loader
        type: "asset".generator: {
          filename: "img/[name].[hash:6][ext]",},parser: {
          dataUrlCondition: {
            maxSize: 1024 * 1024,},},}, {test: /\.ttf|eot|woff2? $/i.// Load the font file
        type: "asset/resource".generator: {
          filename: "font/[name].[hash:6][ext]",},},],},};Copy the code

4. The use of the Plugin

  • Loader is used to convert specific module types

  • Plugins can be used to perform a wide range of tasks, such as packaging optimization, resource management, environment variable injection, etc

  • CleanWebpackPlugin: Manual removal of dist files is required for repackaging. This plugin helps us do this

  • HtmlWebpackPlugin: Help generate HTML files, import the generated files after packaging, of course, you can also choose to customize HTML templates.

  • DefinePlugin: Global constants that allow configuration to be created at compile time and are built into WebPack.

ERROR in Template execution failed: ReferenceError: BASE_URL is not defined The DefinePlugin plugin can be used at this point.

  • CopyWebpackPlugin: This helps us copy files to the dist directory

Project Structure:

├ ─ dist ├ ─ public │ ├ ─ index. The HTML │ └ ─ the favicon. Icon ├ ─ SRC │ ├ ─ CSS │ │ └ ─ file. (CSS | less) │ ├ ─ the font │ │ └ ─ file. (CSS | eot | TFF | woff | woff2}) │ ├ ─ img │ │ └ ─ file. (PNG | JPG) │ ├ ─ js │ │ └ ─ file. The js │ └ ─ main. Js ├ ─. Browserslistrc// Compatible browser version dependent (also configurable in package.json)├ ─ postcss. Config. Js// CSS compatibility related├ ─ wk. A config. Js/ / webpack configuration├ ─ index. The HTML ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

wk.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
  entry: "./src/main.js".output: {
    filename: "js/bundle.js".// The generated dist/js is placed in the js directory
    path: path.resolve(__dirname, "./build"),},module: {
    rules: [{test: /\.css$/, 
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 1,}},"postcss-loader",]}, {test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader".options: {
              importLoaders: 2,}},"postcss-loader"."less-loader",]}, {test: /\.(png|jpe? g|gif|svg)$/,
        type: "asset".generator: {
          filename: "img/[name].[hash:6][ext]",},parser: {
          dataUrlCondition: {
            maxSize: 100 * 1024,},},}, {test: /\.ttf|eot|woff2? $/i,
        type: "asset/resource".generator: {
          filename: "font/[name].[hash:6][ext]",},},],},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "hello webpack".template: "./public/index.html".// Select a template to use. If not specified, use the default EJS template
    }),
    new DefinePlugin({
      BASE_URL: '". /".// Define the global variable BASE_URL
    }),
    new CopyWebpackPlugin({
      patterns: [{from: "public".globOptions: {
            ignore: ["**/index.html"."**/.DS_Store"."**/abc.txt"],},},],};Copy the code

05. Webpack modular

Webpack packages code that allows for a wide variety of modularity, but the CommonJS, ES Module is commonly used

  • CommonJS implementation in Webpack

The project structure

├─ ├─ SRC │ ├─js │ ├─ ├.js// Use CommonJS to export objects│ └ ─ index. Js ├ ─. Browserslistrc, ├ ─ postcss. Config. Js ├ ─ wk. A config. Js ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Wk. A config. Js configuration

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

module.exports = {
  mode: "development".// Default is production
  entry: "./src/index.js".devtool: "source-map".// Make it easy to read the packaged code
  output: {
    filename: "js/bundle.js".path: path.resolve(__dirname, "./build"),},plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin()],
};
Copy the code

View the generated JS file after packaging

// Execute the function immediately
(function () {
  // Define an object
  // Module path (key): function (value)
  var __webpack_modules__ = {
    "./src/js/format.js": function (module) {
      const dateFormat = (date) = > {
        return "2020-12-12";
      };
      const priceFormat = (price) = > {
        return "100.00";
      };
      console.log(cba);
      module.exports = { dateFormat, priceFormat, }; }};// Define an object to load the module cache
  var __webpack_module_cache__ = {};
  function __webpack_require__(moduleId) {
    if (__webpack_module_cache__[moduleId]) {
      return __webpack_module_cache__[moduleId].exports;
    }
    // Assign the same object to the module variable and __webpack_module_cache__[moduleId]
    var module = (__webpack_module_cache__[moduleId] = {
      exports: {},});// Load the execution module
    __webpack_modules__[moduleId](module.module.exports, __webpack_require__);
    Module. exports {dateFormat: function, priceForamt: function}
    return module.exports;
  }
  / /! Symbols parse functions into expressions => Execute the function immediately! (function () {
    const { dateFormat, priceFormat } = __webpack_require__("./src/js/format.js");
    console.log(dateFormat("abc"));
    console.log(priceFormat("abc")); }) (); }) ();Copy the code
  • Implementation of ES Module in Webpack
// 1. Define an object that contains our module mapping
var __webpack_modules__ = {
  "./src/es_index.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
    // The purpose of calling r is to record an __esModule -> true
    __webpack_require__.r(__webpack_exports__);

    // _js_math__WEBPACK_IMPORTED_MODULE_0__ == exports
    var _js_math__WEBPACK_IMPORTED_MODULE_0__ =
      __webpack_require__("./src/js/math.js");

    // Is equivalent to the following code
    // console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.mul(20, 30));
    // console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.sum(20, 30));
    console.log((0, _js_math__WEBPACK_IMPORTED_MODULE_0__.mul)(20.30));
    console.log((0, _js_math__WEBPACK_IMPORTED_MODULE_0__.sum)(20.30));
  },
  "./src/js/math.js": function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
    __webpack_require__.r(__webpack_exports__);

    // Calls the d function: sets a proxy definition for exports
    // Exports objects have no corresponding functions themselves
    __webpack_require__.d(__webpack_exports__, {
      sum: function () {
        return sum;
      },
      mul: function () {
        returnmul; }});const sum = (num1, num2) = > {
      return num1 + num2;
    };
    const mul = (num1, num2) = > {
      returnnum1 * num2; }; }};// 2. Module cache
var __webpack_module_cache__ = {};

// 3. Implement the require function (load module)
function __webpack_require__(moduleId) {
  if (__webpack_module_cache__[moduleId]) {
    return __webpack_module_cache__[moduleId].exports;
  }
  var module = (__webpack_module_cache__[moduleId] = {
    exports: {},}); __webpack_modules__[moduleId](module.module.exports, __webpack_require__);
  return module.exports;
}

!(function () {
  // __webpack_require__ Adds a property: d -> value function
  __webpack_require__.d = function (exports, definition) {
    for (var key in definition) {
      if( __webpack_require__.o(definition, key) && ! __webpack_require__.o(exports, key)
      ) {
        Object.defineProperty(exports, key, {
          enumerable: true.get: definition[key],
        });
      }
    }
  };
})();

!(function () {
  // the __webpack_require__ function object adds a property: o -> function
  __webpack_require__.o = function (obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop); }; }) (); ! (function () {
  // the __webpack_require__ function object adds a property: r -> value function
  __webpack_require__.r = function (exports) {
    if (typeof Symbol! = ="undefined" && Symbol.toStringTag) {
      Object.defineProperty(exports.Symbol.toStringTag, { value: "Module" });
    }
    Object.defineProperty(exports."__esModule", { value: true}); }; }) (); __webpack_require__("./src/es_index.js");
Copy the code

06. Webpack source – the map

  • The code that runs in the browser is usually compressed and is actually different from the code we write. For example, ES6 code might be converted to ES5; The corresponding line number or column number must be inconsistent after compilation; The code name will be modified when the code is uglified; TS code will be converted to JS and so on. Source-map is a mapping from transformed code to the original source file, enabling the browser to refactor the original source and display the reconstructed source in the debugger.
  • There are 26 options available for handling source-Map portals in Webpack. Selecting different values will produce slightly different Source-Maps, and the packaging process will also have performance differences, which can be selected for different situations.
  • False: do not use source-map
  • None: Default value in production mode (nothing is written), does not generate source-map
  • eval: Default value in development mode, source-map is not generated
    • Add //#soourceURL= to the code that eval executes
    • It is parsed by the browser at execution time, and some corresponding file directories are generated in the debug panel for easy debugging of the code.
  • Source-map: A separate source-map file is generated, with a comment pointing to the source-map file in the bundle
  • Eval-source-map: A source-map is generated, but the source-map is appended to the eval function with the DataUrl.
  • Inline-source-map: a source-map is generated, but the source-map is added to the end of the bundle file with the DataUrl.
  • Cheap-source-map: Source-map is generated, but it is more efficient because it does not generate Column Mapping (because in development, only row information is usually needed to locate errors)
  • Cheap-module-source-map: source-map is generated, similar to cheap-source-map but better for sourcemap from loader (babel-loader needs to be configured).
  • Hidden-module-source-map: source-map is generated, but the source-map file is not referenced, which removes the sourcemap reference comment from the package file
  • Nosource-module-source-map: source-map will be generated, but source-map will not have error messages and source files will not be generated
  • A combination of multiple values
    • The inline | hidden | eval: choose three values
    • Nosource: Optional
    • Cheap: Optional and can follow the Module value
    • [inline-|hidden-|eval-][nosources-][cheap-[module-]]souorce-map
  • Source-map /cheap-modules-source-map is recommended
  • Testing phase: source-map/cheap-modules-source-map is recommended
  • Release phase: false/ default (no write)

Project Contents:

├ ─ dist ├ ─ SRC │ ├ ─ js │ │ └ ─ format. The js │ └ ─ index. The js ├ ─. Browserslistrc ├ ─ postcss. Config. Js ├ ─ wk. A config. Js ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Wk. A config. Js configuration

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

module.exports = {
  mode: "development".devtool: "cheap-source-map".// This value can be changed to verify source-map relevance
  entry: "./src/index.js".output: {
    filename: "js/bundle.js".path: path.resolve(__dirname, "./build"),},module: {
    rules: [{test: /\.js$/,
        use: {
          loader: "babel-loader".options: {
            presets: ["@babel/preset-env"],},},},],},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "hello webpack",})]};Copy the code

07. Babel

  • Why Babel?

Babel is a toolchain for older browsers or for ease of converting ESMAScript2015+ code to backwards-compatible versions of JavaScript. Including syntax conversion, source code conversion, Polyfill to achieve the target to alleviate the missing features, etc.

  • How does Babel convert one piece of code (ES6, TS, React) to another (ES5)?

    • You can think of Babel as a compiler
    • The job of the Babel compiler is to convert our source code into another piece of source code that the browser can recognize directly
  • Babel workflow

    • Parsing
    • Transformation phase
    • Code Generation

Specific steps: Source code -> Lexical Analysis -> Token array -> Syntactic Analysis (also known as Parsing) -> AST Abstract syntax tree -> Traverse -> Visitor -> Plugin -> New AST (new Abstract syntax tree) -> Object code

The project structure

├ ─ dist ├ ─ SRC │ ├ ─ react_index. Js │ └ ─ index. The js ├ ─. Browserslistrc ├ ─ Babel. Config. Js ├ ─ postcss. Config. Js ├ ─ wk. A config. Js ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Wk. A config. Js configuration

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

module.exports = {
  mode: "development".entry: "./src/react_index.jsx".devtool: "source-map".output: {
    filename: "js/bundle.js".path: path.resolve(__dirname, "./build"),},module: {
    rules: [{test: /\.jsx? $/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader".// The following section can be separated out into the babel.config.js file
          // options: {
            // presets: [
              / / /
               // "@babel/preset-env",
                // This section is used when.browserslistrc is not configured
                / / {
                  // targets: ["chrome 88"]
                  // esmodules: true
                // },
              / /,
           / /,
            // There is no need to configure plug-ins one by one when using presets
            // plugins: [
            // "@babel/plugin-transform-arrow-functions",
            // "@babel/plugin-transform-block-scoping"
            // ]},},},],},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "coderwhy webpack".template: "./index.html",})]};Copy the code

Babel. Config. Js configuration

module.exports = {
  presets: [["@babel/preset-env", {
      // false: do not polyfill any related code
      // Usage: Refer to the API for any polyfills needed in the code
      // Entry: Manually import core-js/ regenerator-Runtime into the entry file and import all corresponding polyfills based on the target browser
      useBuiltIns: "entry".// Specify the core.js version
      corejs: 3}].// React related presets
    ["@babel/preset-react"]],// plugins: [
  // ["@babel/plugin-transform-runtime", {
  // corejs: 3
  / /}]
  // ]
}
Copy the code

polyfill: Patches to help us use JavaScript better, such as the syntax feature eg: Promise, Generator, Symbol, etc., are incompatible with these features and will inevitably report an error. In this case, you can use polyfill or patch to include this feature.

  • For more information, see: Portal

08. Webpack compiler TypeScript related

The project structure

├ ─ dist ├ ─ SRC │ ├ ─ react_index. Js │ └ ─ index. The js ├ ─. Browserslistrc ├ ─ Babel. Config. Js ├ ─ postcss. Config. Js ├ ─ wk. A config. Js ├ ─ tsconfig. Json ├ ─ package - lock. Json └ ─ package. The jsonCopy the code

Generate tsconfig.json using the command TSC –init

The wk.config.js configuration is as follows

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

module.exports = {
  mode: "development".entry: "./src/index.ts".devtool: "source-map".output: {
    filename: "js/bundle.js".path: path.resolve(__dirname, "./build"),},module: {
    rules: [{test: /\.ts$/,
        exclude: /node_modules/.// Is inherently dependent on typescript.
        // use: "ts-loader" 
        use: "babel-loader",}]},plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "hello webpack".template: "./index.html",})]};Copy the code

Babel. Config. Js configuration

module.exports = {
  presets: [["@babel/preset-env",
      {
        useBuiltIns: "usage".corejs: 3,}], ["@babel/preset-typescript"]],};Copy the code
  • Ts-loader and Babel-loader options
    • ts-loader

      • Compiling TypeScript directly only converts TS to JS
      • If you want to add a corresponding polyfill to this process, then ts-Loader cannot do anything about it
      • Babel is also needed to fill polyfill
    • babel-loader

      • Compiling TypeScript directly, you can only convert TS to JS and polyfill
      • However, babel-loader does not check for type errors during compilation

    Best practice: The configuration is added in the package.json script

  "scripts": {
    "build": "webpack --config wk.config.js"."type-check": "tsc --noEmit"."type-check-watch": "tsc --noEmit --watch"
  }
Copy the code

09. ESLint and Prettier

What is ESLint?

  • ESLint is a static program analysis tool (that analyzes code without any executable programs)
  • ESLint can help us establish a uniform code specification in a project to keep it correct, consistent code style, and improve code readability and maintainability
  • ESLint rules are configurable and you can customize your own rules

ESLint file parsing (default creation environment)

  • Env: run in an environment such as a browser, and we will use es2021 (ecmaVersion 12) syntax

  • Extends: Extends the current configuration to extend it from other configuration information. This extends to strings or data (multiple).

  • ParseOptions: Here you can specify the ECMAScript version, the sourceType type

    • Parse: The default is Espree (also a JS Parser for ESLint), but since we need to compile TS, we need to specify the corresponding interpreter
  • Plugins: Specify the plugins to use

  • Rules: User-defined rules

    • The value of the configured rule name can be a number, string, or array
    • The string corresponds to three values: off, WARN, and error
    • Numeric values: 0, 1, 2 (corresponding to the above)
    • Array we can tell the corresponding hint and the value we want to get for example: [‘error’, ‘double’]

* VSCode ESLint: NPM run ESLint would be a bit cumbersome if you used THE VSCode plugin for Prettier every time you checked: To help you fix these problems automatically, you can either fix them on auto-save or you can use option + Shift + f. If you want to use es-Lint when compiling code, you can use eslint-Loader

  • Webpack packs vue files: You need to install them firstvue-loaderandvue-template-compilerVue LoaderCheck how to configure it.

10. DevServer and HMR

Why build a local server? The code we are developing now needs two operations to run: 1. Num run build compile package 2. Viewing the results through live Server or directly through the browser to index.html is a very frustrating development experience and we want to be able to automatically compile and display the files as they change. Webpack Watch mode 2. Webpack-dev-serve 3. Webpack-dev-middleware

  • Watch mode

    • In this mode, if only one of the files that depend on the Webpack diagram is updated, the code will be recompiled instead of having to manually build
    • Enable mode: Add it in Export configurationWatch: true,Or add it in the command to start webpack --watch
  • webpack-dev-server

    • The above method can listen for file changes, but it doesn’t automatically refresh the browser itself. Of course, we can do this using live-server in vscode. However, live reloading (real-time reloading) can be enabled if you want to do so without a live-server.
    • Install webpack dev – server:npm install --save-dev webpack-dev-server
    • Use webpack-dev-server: does not write to any output files after compilation, but saves the bundle files to memory
  • Webpack-dev-middleware is a wrapper that sends webPack-processed files to a server

    • Webpack-dev-server uses it internally, but it can be used as a separate package for more customization as needed

Understanding hot Module Replacement (HMR)

  • What is HMR?

    • HMR stands for Hot Module Replacement
    • Module hot replacement is the process of replacing, adding, and removing modules while the application is running without having to refresh the entire page
  • HMR speeds up development in several ways

    • Do not reload the entire page, which preserves the state of some applications
    • Save development time by only updating what needs to be changed
    • If you modify the CSS, the JS source code is immediately updated in the browser, which is equivalent to modifying the style directly in the browser’s DevTools
  • How to use HMR

    • By default, webpack-dev-server already supports HMR, so we just need to turn it on
    • Without HMR enabled, the entire page will refresh automatically after we modify the source code, using live reloading
    • Open the HMR: Modifies the WebPack configurationdevServer: { hot: true } However, when you use it, you will find that when you change the code in one place, the whole page is still refreshed becauseWhen you need to specify which modules are updatedFor HMR
if (module.hot) {
  module.hot.accept("./file.js".() = > {
    // This function is equivalent to a hook function
    console.log("File module has been updated ~");
  });
}
Copy the code
  • Framework of HMR

    • Do you often need to manually write the module.hot.accpet API during development? In fact, there are very mature solutions in the community for these situations: for example, vUE development uses vue-loader, which supports THE HMR of VUE components; The React Hot Loader calls the React component in real time. The React Hot Loader calls the React Hot Loader in real time.
  • The Vue HMR

    • Vuve-loader is used to load vue, and components loaded by VuVE-Loader perform HMR processing by default
    • Install the dependencies needed to load vUE npm install vue-loader vue-template-compiler -D
  • The React of HMR

    • React Hot Loader (HMR) {react-refresh (HMR) {react-refresh (HMR) {react-refresh (HMR)}
    • Install @ PMMMWH/react – refresh – webpack – the plugin

    nnpm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh

Webpack. Config. Js configuration

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");

module.exports = {
  // watch: true,
  mode: "development".entry: "./src/index.js".output: {
    filename: "bundle.js".path: path.resolve(__dirname, "./build"),},// Specifically for webpack-dev-server
  devServer: {
    hot: true.port: 9000,},module: {
    rules: [{test: /\.jsx? $/i,
        use: "babel-loader"}, {test: /\.vue$/i,
        use: "vue-loader"}, {test: /\.css/i,
        use: ["style-loader"."css-loader"],},],},plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",}).new ReactRefreshWebpackPlugin(),
    new VueLoaderPlugin(),
  ],
};

Copy the code

Babel. Connfig. Js configuration

module.exports = {
  presets: [["@babel/preset-env"],
    ["@babel/preset-react"]],plugins: [["react-refresh/babel"]]}Copy the code
  • The principle of HMR

    • So how does HMR work? How can you update the contents of only one module?
      • Webpack-dev-server creates two services: a static resource service (Express) and a Socket service (net.socket);
      • Express Server is responsible for providing static resources directly (packaged resources are directly requested and parsed by browsers).
    • HMR Socket Server, which is a Socket long connection:
      • One of the best benefits of a long connection is that the two sides can communicate after the connection is established (the server can send files directly to the client).
      • Json (manifest file) and.js (Update chunk) files are generated when the server detects that the corresponding module changes.
      • Through the long connection, you can directly send these two files to the client (browser).
      • After the browser gets the two new files, the HMR Runtime mechanism loads the two files and updates them for the modified modules.
  • For more information, see this article portal

11. Processing of paths in webpack

  • The purpose of path in Output is to tell the output directory after Webpack

    • For example, static resources js, CSS output to where, usually set to dist, build folder, etc
  • Output also has a publicPath attribute, which specifies a base path to be referenced by the index.html file package

    • Its default value is an empty string, so when we package and import the JS file, the path is bundle.js
    • If you set it to/and the path is /bundle.js, the browser will request the resource based on the domain + path.
    • If you want to run the HTML file locally, you set it to././bundle.js, and you can look up resources based on the relative path
  • DevServer also has a publicPath property that specifies the folder where the local service is located:

    • The default value is /, which means we can access the resource http://localhost:8080 directly by accessing the port
    • If it is set to/ABC, so we need through http://localhost:8080/abc to access to the corresponding resources after packaging
    • And at this point, our bundle.js passedhttp://localhost:8080/bundle.jsIt’s also inaccessible
      • So output.publicPath must also be set to/ABC;
      • Devserver. publicPath is recommended to be the same as output.publicPath
  • The contentBase in devServer doesn’t really have much use for direct access to a packaged resource. What it does is if the packaged resource depends on some other resource, then you have to specify where to look for that content

    • For example, in index.html, we need to rely on an abc.js file that we store in a public file.

    • ;

      • But the browser can’t find the folder through a relative path when it’s packaged like this;

      • So the code looks like this:

      • But how do we tell it to find out that this file exists? Set contentBase;

    • There is, of course, a property in devServer that listens for recompilation of contentBase changes: watchContentBase

  • In devServer hotOnly is whether to refresh the entire page when the code fails to compile:

    • By default, when code compilation failures are fixed, we refresh the entire page;

    • If you don’t want to refresh the entire page, you can set hotOnly to true.

  • DevServer host set host address:

    • The default is localhost
    • Set it to 0.0.0.0 if you want access elsewhere
  • Localhost 0.0.0.0

    • Localhost: Is essentially a domain name that will normally be resolved to 127.0.0.1

    • 127.0.0.1: Loop Back Address, which means that packets sent by our host are received directly

      • Normal database packets go through: Application layer > Transport layer > Network layer > Data link layer > physical layer
      • The loopback address, however, is directly obtained at the network layer and does not pass through the data link layer and physical layer
      • For example, when we listen on 127.0.0.1, hosts on the same network segment cannot be accessed by IP address
    • 0.0.0.0: Listen for all addresses on IPV4 and find different applications by port

      • For example, when we listen for 0.0.0.0, in the same network segment on the host, it is accessible by IP address
  • Port Specifies the listening port. The default is 8080

  • Open Indicates whether to open the browser

    • The default is false, and setting it to true opens the browser
    • It can also be set to equivalent to Google Chrome
  • Compress Whether to enable Gzip compression for static files: The default value is false and can be set to true

  • ProxyThe agent
    • Proxy is a very common configuration option in our development. Its purpose is to set up proxy to solve the problem of cross-domain access: for example, one of our API requests is http://localhost:8888, but the domain name of the local startup server is: http://localhost:8000 at this time to send network requests will appear cross-domain problems, so we can send the request to a proxy server, proxy server and API server do not have cross-domain problems, we can solve our cross-domain problems; We can do the following.

    • Target: represents the agent to the destination address, such as/API – webpack/moment will be acting to http://localhost:8888/api-webpack/moment;

    • PathRewrite: By default, our /api-webpack will also be written to the URL. If you want to remove it, you can use pathRewrite;

    • Secure: by default, servers that do not receive and forward HTTPS packets can be set to false.

    • ChangeOrigin: Indicates whether to update the host address of headers requested by the agent.

eg:

    proxy: {
      "/api-webpack": {
        target: "http://localhost:8888".pathRewrite: {
          "^/api-webpack": "",},secure: false.changeOrigin: true,}},// http://localhost:8888/api-webpack/moment => http://localhost:8888/moment
Copy the code
  • HistoryApiFallback is a very common attribute in development. Its main function isto solve the error of 404 returned when the SPA page is refreshed after a route jump

    • Boolean value: false by default; If set to true, the contents of index.html are automatically returned when a 404 error is returned on refresh.
    • The value of object can be configured with the rewrites property: p Can be configured with from to match the path and determine which page to jump to.
    // historyApiFallback: true
    historyApiFallback: {
      rewrites: [{ from: /xxx/, to: "/index.html"}],},Copy the code
  • Resolve module resolution

    • Resolve helps WebPack find code from each require/import statement that needs to be imported into the appropriate module

    • Webpack uses enhanced-resolve to resolve file paths

    • Webpack resolves files in three different paths

      • Absolute path: Since the absolute path to the file is already obtained, no further parsing is required
      • Relative path: the directory where the import or require resource files are located, which is considered a context directory; The relative path given in import/require will concatenate this context path to generate the module’s absolute path
      • Module path: retrieve modules from all directories specified in resolve.modules (default is [‘node — modules’], so files are looked up from node_modules by default); We can replace the initial module path with an alias
  • Determine whether it is a file or folder

    • If it is a file:

      • If the file has an extension, it is packaged directly
      • Otherwise, the resolve.extendsions option will be used for file extension resolution
    • If it’s a folder

      • The resolve.mainFiles configuration option looks up files in the folder in the order specified in the resolve.mainFiles configuration option

        • The default value for resolve.mainFiles is [‘index’]
        • Resolve. Extendsions
  • Extendsions and alias configuration

    • Extendsions are automatically added extensions that parse into files

      • The default is **[‘.wasm’, ‘.mjs’, ‘.js’, ‘.json’]**
      • So if you want to add.vue or.jsx or ts files to your code, you have to add the extension manually
    • Another very useful feature is configuring aliases

      • When the directory structure of our project is deep, it is likely that ‘.. /.. /.. /’, where we can use path aliases
  resolve: {
    extensions: [".wasm".".mjs".".js".".json".".jsx".".ts".".vue"].alias: {
      "@": path.resolve(__dirname, "./src"),
      pages: path.resolve(__dirname, "./src/pages"),}},Copy the code

12. Environment separation for Webpack

  • How to distinguish development environments: 1. Write two different configuration files and load the different configuration files during development and generation. 2. Write the same entry profile and set parameters to distinguish between them
"scripts": {
    "build": "webpack --config ./config/webpack.prod.js"."serve": "webpack serve --config ./config/webpack.dev.js"."build2": "webpack --config ./config/webpack.common.js --env production"."serve2": "webpack serve --config ./config/webpack.common.js --env development"
  },
Copy the code

The project structure

├─ Build ├─config │ ├─ ├─ ├.env.conf. Js │ ├─ ├─ SRC │ ├─page │ ├─ ├.env.conf ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ ├─ download.jsonCopy the code
  • Import file parsing

    • The previous rule for writing entry files was./ SRC /index.js, but if the location of our configuration file becomes config directory, should it become.. / SRC/index. Js

      • / SRC /index.js; / SRC /index.js;
      • This is because the entry file is actually the context associated with another property
    • Context is used to parse entry points and loaders:

      • Official: Default is current path (but after testing, default should be webpack startup directory)

      • It is also recommended to pass a value in the configuration; path.resolve()

// Context is the directory where the configuration file resides
modules.exports = {
    context: path.resolve(__dirname, ". /"),
    entry: ".. /src/index.js"
}
// context is the previous directory
modules.exports = {
    context: path.resolve(__dirname, ". /"),
    entry: "./src/index.js"
}
Copy the code

Take a look at the contents of each configuration file

  • path.js
const path = require('path');
// API in node -> is used to obtain the current command execution path
const appDir = process.cwd();
const resolveApp = (relativePath) = > path.resolve(appDir, relativePath);
module.exports = resolveApp;
Copy the code
  • webpack.common.js
const resolveApp = require("./paths");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const { merge } = require("webpack-merge");
const prodConfig = require("./webpack.prod");
const devConfig = require("./webpack.dev");

const commonConfig = {
  entry: "./src/index.js".output: {
    filename: "bundle.js".path: resolveApp("./build"),},resolve: {
    extensions: [".wasm".".mjs".".js".".json".".jsx".".ts".".vue"].alias: {
      "@": resolveApp("./src"),
      pages: resolveApp("./src/pages"),}},module: {
    rules: [{test: /\.jsx? $/i,
        use: "babel-loader"}, {test: /\.vue$/i,
        use: "vue-loader"}, {test: /\.css/i,
        use: ["style-loader"."css-loader"],},],},plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html",}).new VueLoaderPlugin(),
  ]
};

module.exports = function(env) {
  const isProduction = env.production;
  process.env.NODE_ENV = isProduction ? "production": "development";

  const config = isProduction ? prodConfig : devConfig;
  const mergeConfig = merge(commonConfig, config);

  return mergeConfig;
};
Copy the code
  • webpack.dev.js
const resolveApp = require('./paths');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

const isProduction = false;

console.log("Load devConfig config file");

module.exports = {
  mode: "development".devServer: {
    hot: true.hotOnly: true.compress: true.contentBase: resolveApp("./why"),
    watchContentBase: true.proxy: {
      "/api-webpack": {
        target: "http://localhost:8888".pathRewrite: {
          "^/api-webpack": ""
        },
        secure: false.changeOrigin: true}},historyApiFallback: {
      rewrites: [{from: /abc/, to: "/index.html"}}},plugins: [
    // Development environment
    new ReactRefreshWebpackPlugin(),
  ]
}
Copy the code
  • webpack.prod.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const isProduction = true;

module.exports = {
  mode: "production".plugins: [
    // Build the environment
    new CleanWebpackPlugin({}),
  ]
}
Copy the code
  • babel.config.js
const presets = [
  ["@babel/preset-env"],
  ["@babel/preset-react"]];const plugins = [];
const isProduction = process.env.NODE_ENV === "production";

// React HMR -> Hot replacement of modules must be implemented at development time
if(! isProduction) { plugins.push(["react-refresh/babel"]);
} else{}module.exports = {
  presets,
  plugins
}
Copy the code

13. Code separation for Webpack

  • Code Splitting is a very important feature of WebPack

    • The main purpose is to separate the code into bundles that can then be loaded on demand or in parallel
    • For example, by default, all JavaScript code (business code, third party dependencies, other modules not currently used) is loaded on the home page, which will affect the loading speed of the home page
    • Code separation provides better code loading performance by splitting up smaller bundles and controlling resource loading priorities
  • There are three common types of code separation in Webpack

    • Entry starting point: Configure manual separation of code using Entry
    • Prevent duplication: Use Entry Dependencies or SplitChunksPlugin to decouple code
    • Dynamic import: Separation of code through inline function calls to modules
  • Multi-entry Starting point: Entry Configures multiple entry points

  entry: {
    main: "./src/main.js".index: "./src/index.js",}Copy the code
  • If both indx.js and main.js depend on two libraries: Lodash, day.js. If we just separate the entries, then both bundles will have a copy of LoDash and Day.js that we can actually share.
  entry: {
    main: { import: "./src/main.js".dependOn: "shared" },
    index: { import: "./src/index.js".dependOn: "shared" },
    shared: ["lodash"."dayjs"]}Copy the code
  • SplitChunks: This is implemented using SplitChunksPlugin

    • The plug-in WebPack is already integrated by default, so you don’t need to install it again
    • You only need to provide configuration information about SplitChunksPlugin
  • SplitChunks custom configuration parse portal

    • Chunks: the default async only processes asynchronously imported code; Initial: Only synchronously imported code is processed; All: Processes both synchronous and asynchronous code

    • MinSize: The size of the package should be at least minSize. If a package cannot be split up to minSize, the package will not be split up

    • MaxSize: Split a package larger than maxSize into a package larger than minSize

    • MinChunks: specifies the minimum number of introduced chunks. The default value is 1. If the value is set to 2 and the number of introduced chunks is 1, the chunks will not be split separately

    • Name: Name of the unpack. This parameter can be set to false. You need to set the name in cacheGroups

    • CacheGroups: Groups split packages. For example, a Lodash package is not packaged immediately after it is split, but will wait until there are other packages that meet the rule to be packaged together

      • Test property: Matches packages that match the rule
      • Name attribute: Name of the subcontract
      • Filename property: The name of the subcontract, optionally using placeholder property
      • Priority attribute: A module can belong to multiple cache groups. Optimization gives priority to cache groups with higher priorities. The default group has a negative priority to allow custom groups to get higher priority (default for custom groups is 0)
  • Dynamic Import

    • Another method of code splitting is dynamic import, and WebPack provides two ways to implement dynamic import

      • This is done using the import () syntax in ECMAScript, which is currently recommended
      • Webpack’s legacy Require. Ensure is no longer recommended
    • For example, we have a module called bar.js

      • This is a module that we want to load at runtime (e.g., when a judgment condition is true)
      • Since we are not sure that the code in this module will be used, it is best to split it into a separate JS file
      • This ensures that the browser does not need to load and process the js code for the file when the content is not used here
      • You can use dynamic imports at this point
    • Note: Use dynamic import bar.js

      • In WebPack, you import an object dynamically
      • The actual exported content is in the default property of the object, so a simple deconstruction is required
  • Name of the dynamically imported file

    • Dynamic imports are always packaged as separate files and are not configured in cacheGroups, so they are usually named in output via the chunkFilename attribute

        output: {
          path: resolveApp("./build"),
          filename: "[name].bundle.js".chunkFilename: "[name].[hash:6].chunk.js",}Copy the code
    • By default, we get [name] the same as the name of the id. If we want to change the name, we can do magic comments:

      import(/* webpackChunkName: "bar" */ "./bar").then(({default: bar}) = > {
          console.log(bar)
      });
      Copy the code
  • ChunkIds configuration: Tells you which algorithm to use for the ID of the Webpack module. Three common configurations are as follows

    • Natural: Use ids in numeric order
    • Named: Default for development, id of a readable name
    • Deterministic: a short numeric ID that is invariant across compilations (webpack doesn’t have this value; If you were using Natual at that time, you would have problems when some compilations changed.)
    • Recommended during developmentnamed; It is recommended for packagingdeterministic
  • RuntimeChunk configuration:

    • Configures whether runtime code is extracted into a separate chunk

      • Runtime code values refer to the code associated with parsing, loading, and module information in the runtime environment
      • The runtime code is used to load the code associated with the import function
    • A cache strategy that benefits the browser when pulled out

      • If we modify the business code (main), then the Runtime and Component, bar, chunk do not need to be reloaded
      • For example, if we modify the code in component bar, the code in main does not need to be reloaded
    • Set the value of the

      • True /multiple: Package one Runtime file for each entry
      • Single: Package a Runtime file
      • Object: The name attribute determines the name of runtimeChunk

The code shows the main configuration modules

  output: {
    path: resolveApp("./build"),
    filename: "[name].bundle.js".chunkFilename: "[name].[hash:6].chunk.js",},// use natural numbers (not recommended),
  // named: use the directory where the package resides as name(recommended in development environments)
  // Deterministic: id generation. Ids generated for the same file are unchanged
  // chunkIds: "deterministic",
  optimization: {
    // Perform compression operations on the code
    minimizer: [
      new TerserPlugin({
        extractComments: false,})],splitChunks: {
      chunks: "all".minSize: 20000.maxSize: 20000.minChunks: 1.cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          filename: "[id]_vendors.js".priority: -10,},default: {
          minChunks: 2.filename: "common_[id].js".priority: -20,}}},runtimeChunk: {
      name: function(entrypoint) {
        return `webpack-${entrypoint.name}`; }},}Copy the code
  • PrefetchandPreload
    • Use these built-in directives to tell the browser when you declare an import

      • Prefetch: Resources that may be required for some future navigation
      • Preload: Resources that may be required in the current navigation
    • The preload directive differs in a number of ways from the Prefetch directive

      • Preload Chunk starts loading in parallel when the parent chunk loads. Prefetch Chunk starts loading after the parent chunk finishes loading
      • Preload Chunk has medium priority and is downloaded immediately. Prefetch Chunk downloads while the browser is idle
      • The preload chunk is requested immediately in the parent chunk for the present moment. Prefetch Chunk will be used at some point in the future.
    • Usage – Magic notes: /* webpackPrefetch: true */ or /* webpackPreload: true */

14. The CDN and shimming

  • What is a CDN?

    CDN calls it the Content Delivery Network

    It refers to using the server closest to each user through an interconnected network system

    Faster and more reliable delivery of static resources to users

    To provide high performance, scalability and low cost delivery of web content to users

  • In research and development, there are two main ways to use CDN

    • Package all static resources and put them into the CDN server. All resources of users are loaded through the CDN server
    • Some third-party resources are put on the CDN server
  • How to use CDN in Webpack

    • You can modify the webPack configuration directlypublicPath: publicPath: "https://xxx.com/cdn",
  • CDN server of the third-party library

    How to introduce third-party CDN into the project? When packaging, we don’t need to package libraries like Lodash or DayJS into HTML modules, we need to add the corresponding CDN server address

<! - the ejsifJudgment - > < %if (process.env.NODE_ENV === 'production') { %> 
  <script src="https://unpkg.com/[email protected]/dayjs.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
  <% } %> 
  
  externals: {
    // window._
    lodash: "_".// window.dayjs
    dayjs: "dayjs"
  },
Copy the code
  • Shimming: Shippers, which are basically padding our code with shippers to deal with issues such as relying on a third party library that itself relies on LoDash, but does not import LoDash by default. Then we can use ProvidePlugin to achieve shimming effect

  • Note: Webpack does not recommend casual use of Shimming

    • The whole idea behind WebPack is to make front-end development more modular
    • That is, you need to write modules that are closed and do not have implicit dependencies (such as global variables) that are isolated from each other
  • The effect of shimming can be achieved using the ProvidePlugin

    • The ProvidePlugin helps us get a package from a variable per module
    • If WebPack sees this module, it will introduce it in the final bundle;
    • In addition, ProvidePlugin is a default plug-in for WebPack, so there is no need to import it
  // When a variable is not found in the code, we will automatically import the corresponding library through ProvidePlugin
   new webpack.ProvidePlugin({
     axios: "axios".get: ["axios"."get"]})Copy the code

15.MiniCssExtractPlugin and Hash and DLL

  • The MiniCssExtractPlugin helps us extract CSS into a separate CSS file. This plugin needs to be available in webpack4+

How to configure it?

  plugins: [
    // Production environment
    new CleanWebpackPlugin({}),
    new MiniCssExtractPlugin({
      filename: "css/[name].[hash:8].css"})]// webpack.common.js
module: {
  rules: [{test: /\.css/i.// style-lodader -> development
      use: [
        isProduction ? MiniCssExtractPlugin.loader : "style-loader"."css-loader",],},],},Copy the code
  • Hash, ContentHash, and ChunkHash

    There are some attributes in placeholder that are similar. The hash itself is processed by MD4’s hash function to generate a 128-bit hash value (32 hex bits).

    • The hash value is relevant to the overall project

      • Suppose we now have two entries index.js and main.js
      • They’re going to be exported to different bundles, and we’re using hash in the file name
      • If you change the contents of the index.js file at this point, the hash will change
      • That means the names of both files will change
    • Chunkhash solves this problem effectively by parsing different entries to generate hash values

      • For example, if we modify index.js, the chunkhash of main.js will not change
    • Contenthash indicates the hash name of the generated file, which depends only on the content

      • For example, our index.js introduces a style. CSS, which is then extracted into a separate CSS file
      • The CSS file is named if we use chunkhash
      • So when the contents of the index.js file change, the CSS file name will also change
      • At this point we can use contenthash
  • DLL

    • DLL full name is Dynamic Link Library (DLL), is for software in Windows to achieve a shared function Library implementation

    • Webpack also has built-in DLL capabilities, by which he means we can extract shared, infrequently changed code into a shared library

    • This library will be introduced into the code of other projects later in the compilation process

    • The use of DLL libraries is divided into two steps

      • Package a DLL library
      • DLL libraries are introduced into the project
    • Note: Both React and Vue scaffolding have removed DLL libraries after the WebPack4 upgrade (WebPack4 already provides sufficient performance, no DLL is needed anymore)

    • How to package a DLL library?

      Webpack helps us with a built-in DllPlugin that can help us package a DLL library file

    The project structure

    ├─ ├─ ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txtCopy the code
    • Webpack. DLL. Js configuration
    const path = require('path');
    const webpack = require('webpack');
    const TerserPlugin = require('terser-webpack-plugin');
    module.exports = {
      entry: {
        react: ["react"."react-dom"]},output: {
        path: path.resolve(__dirname, "./dll"),
        filename: "dll_[name].js".library: 'dll_[name]'
      },
      optimization: {
        minimizer: [
          new TerserPlugin({
            extractComments: false}})],plugins: [
        new webpack.DllPlugin({
          name: "dll_[name]".path: path.resolve(__dirname, "./dll/[name].manifest.json")]}})Copy the code
    • Use packaged DLL libraries

      If we use react and react- DOM in our coding, and we have splitChunks, they’re going to subcontract them into a separate chunk

      • But now that we have DLL-react, we don’t need to package them separately. We can reference dlL-react directly

      • Step 1: Tell us to use the DLL library via the DLLReferencePlugin

      • Step 2: Introduce our packaged DLL library into the HTML module via the AddAssHtmlPlugin plugin

      plugins: [
        new HtmlWebpackPlugin({
          template: "./index.html",}).new webpack.DllReferencePlugin({
          context: resolveApp(". /"),
          manifest: resolveApp("./dll/react.manifest.json")}),new AddAssetHtmlPlugin({
          filepath: resolveApp('./dll/dll_react.js')})],Copy the code

16. Use of Terser in webpack

  • Terser profile

    • Terser is a JavaScript interpreter (parse), Mangler /Compress tool set
    • Uglip-js was used earlier to compress and uglify our JavaScript code, but it is no longer maintained and does not support ES6+ syntax
    • Terser comes from uglip-es fork and retains most of his Api from the original as well as adapting uglip-es and uglify-js@3 etc
    • In other words, Terser helps us compress, uglify our code, and make our bundles smaller
    • Installation:npm install terser -g or npm install terser
  • The command line uses Terser

    • terser [input files] [options]

    eg: terser abc.js -o abc.min.js -c -m

  • Options for Compress and Mangle

    • compress options:

      • Arrows: Functions in the class or object are converted to arrow functions
      • Arguments: Converts arguments[index] to the corresponding parameter name in a function
      • Dead_code: Removing unreachable code
      • Other portals
    • mangle options

      • Toplevel: Default is false, variable name in top-level scope, uglified (transformed)
      • Keep_classnames: The default value is false, whether to keep the dependent classname
      • Keep_fnames: the default value is false, whether to keep the original function name

    npx terser ./src/abc.js -o abc.min.js -c arrows,arguments=true,dead_code -m toplevel=true,keep_classnames=true,keep_fnames=true

  • How do I use that Terser in webpack

    In real development, you don’t need to manually process the code through Terser, you can use WebPack

    • There is a minimize property in Webpack that defaults to using the TerserPlugin to process your code in production mode

    • If you are not satisfied with the default configuration, you can create an instance of the TerserPlugin and override the relevant configuration

    • First we need to turn on Minimiize to compress our code (it’s already on in production mode by default)

    • Second, we can create a TerserPluginn in Minimizer

    • ExtractComments: defaults to true to extractComments to a separate file

      • We can set this to false if we don’t want to keep this comment during development
    • Parallel: Uses multiple processes to run concurrently to improve build speed. The default number of concurrent runs is os.cpus().length-1, and you can set your own number, but the default value is sufficient

    • TerserOptions: Set our Terser-related configuration

      • Compress: Sets options related to compression
      • Mangle: Set uglification related options, can be directly set to true
      • Toplevel: Whether the underlying variable is converted
      • Keep_classnames: reserved classnames
      • Keep_fnames: Reserve the function name
  optimization: {
    minimize: true.minimizer: [
      new TerserPlugin({
        parallel: true.extractComments: false.terserOptions: {
          compress: {
            arguments: true.dead_code: true,},mangle: true.toplevel: true.keep_classnames: true.keep_fnames: true,},}),],},Copy the code
  • The compression of CSS

    • CSS compression usually removes useless whitespace, etc., because it is difficult to change the names of selectors, properties, values, etc.

    • CSS minimizer-webpack-plugin: Minimizer-webpack-plugin;

    • Css-minimizer-webpack-plugin uses csSNano to optimize and compress CSS (can also be used alone).

    How to use it?

    Step 1: Install the CSS-Minimizer-webpack-plugin

    Step 2: Configure (or plugis) new CssMinimizerPlugin() in optimization.minimizer.

  • What is Scope collieries?

    • Scope adds a new function starting with Webpack3;

    • The function is to enhance the scope and make webPack code smaller and faster;

  • By default, the WebPack package has many function scopes, including some (such as the outermost) IIFE:

    • There are a number of functions that need to be executed from the very beginning of the code to the loading of a module;

    • Scope collieries can combine functions into a module to run.

  • Scope is very simple to use, webpack has built-in corresponding modules:

    • In Production mode, this module is enabled by default;

    • In development mode, we need to open the module ourselves;

    new webpack.optimize.ModuleConcatenationPlugin()

17.Tree Shaking

What is Tree Shaking?

  • Tree Shaking is a term for eliminating dead code in a computer;

  • The original idea, which originated in LISP, was to eliminate uncalled code (pure functions have no side effects and can be safely eliminated, which is one of the reasons why we should try to use pure functions in functional programming).

  • Tree Shaking has since been applied to other languages, such as JavaScript and Dart;

  • Tree Shaking in JavaScript

    • Tree Shaking for JavaScript comes from the packaging tool rollup;

    • This is because Tree Shaking relies on static parsing of ES Modules (no code is executed and Module dependencies are known);

    • Webpack2 officially has built-in support for ES2015 modules and the ability to detect unused modules;

    • This capability is formally extended in WebPack4 and is tagged with the sideEffects property of package.json to tell WebPack where files can be safely deleted at compile time;

    • Webpackage 5 also provides support for some of the CommonJS tree shaking.

  • How to implement Tree Shaking in WebPack? Webpack actually implements Tree Shaking in two different ways

    • UsedExports: Indicates whether certain functions are used and then optimizes them using Terser
    • SideEffects: skip the entire module/file to see if the file has sideEffects
  • usedExports

    • Set mode to Development mode:

      • To see the effects of usedExports, we need to set it to Development mode

      • Because in Production mode, some of the optimizations that WebPack defaults to can have a big impact.

    • Set usedExports to true and false to compare the packaged code:

      • When usedExports is set to true, the unused Harmony export mul;

      • What is the point of this comment? Tell Terser that this code can be removed during optimization;

    • At this point, we will minimize the set true:

      • When usedExports is set to false, the muL function is not removed.

      • When usedExports is set to true, the muL function is removed.

    • Therefore, usedExports implements Tree Shaking in conjunction with Terser.

  • sideEffects

    • SideEffects is used to tell the webpack compiler which modules have sideEffects:

      • The side effect means that the code performs some special tasks, so the significance of this code cannot be judged only by export.
    • Set the value of sideEffects in package.json:

      • If we set sideEffects to false, we are telling Webpack that it is safe to remove unused exports;

      • If there’s some we want to keep, we can set it to an array;

    • For example, we have a format.js, style.css file:

      • The file is imported without any variables to accept;

      • Then the packaged file will not retain any code related to format.js and style.css.

      sideEffects: [
          "format.js"."*.css"
      ]
      Copy the code
  • Configuring Tree Shaking in Webpack

    • Set usedExports to true in Optimization to help Terser optimize.

    • Configure sideEffects in package.json to optimize modules directly;

  • Tree Shaking in CSS

    The above are Tree Shaking for JavaScript. Is it possible to Tree Shaking for CSS?

    • Tree Shaking for CSS requires some help from other plugins;

      • In the early days, we used the PurifyCss plugin to do tree shaking for CSS, but the library is no longer maintained

      • There’s another library that you can use to complete the Tree Shaking: PurgeCSS for CSS. It’s also a tool that helps remove unused CSS.

      • NPM install PurgeCss -webpack-plugin -d install PurgeCss -webpack-plugin -d

  • Configuring PurgeCss (production Environment)

    • Paths: To check which directories need to be analyzed, use glob.

    • By default, Purgecss removes the style of our HTML tags. If we want to keep it, we can add a safelist property.

    • Purgecss also handles less files (so it’s tree shaking of packaged CSS);

    new PurgeCssPlugin({
      paths: glob.sync(`${resolveApp("./src")}/ * * / * `, {nodir: true}),
      safelist: function() {
        return {
          standard: ["body"."html"]}}})Copy the code

18. Code compression in Webpack

  • What is HTTP compression

    • HTTP compression is built in between the server and client to improve transmission speed and bandwidth utilization;
  • What is the flow of HTTP compression?

    Step 1: HTTP data is compressed before the server sends it; (This can be done in Webpack)

    Step 2: When a compatible browser sends a request to the server, it tells the server which compression format it supports.

    Step 3: The server directly returns the corresponding compressed file in the browser supported compression format, and informs the browser in the response header;

  • Current compression format

    • Compress – a method of UNIX’s compress program (not recommended for most applications for historical reasons, gzip or deflate should be used instead);

    • Deflate – Compression based on the Deflate algorithm (defined in RFC 1951), encapsulated in the Zlib data format;

    • Gzip – GNU Zip format (defined in RFC 1952), is the most widely used compression algorithm at present;

    • Br — a new open source compression algorithm designed for encoding HTTP content;

  • Webpack compresses files

    • The first steps to HTTP compression are implemented in Webpack, and we can use the CompressionPlugin.

    • First, install CompressionPlugin: NPM install compression-webpack-plugin -d

    • The second step is to use CompressionPlugin

    new CompressionPlugin({
      test: /\.(css|js)$/i.// Matches which files to compress
      threshold: 0.// Compression starts from the file age
      minRatio: 0.8.// Minimum compression ratio
      algorithm: "gzip".// The compression algorithm used
      // exclude
      // include
    }),
    Copy the code
  • Compression of code in HTML

    • The HtmlWebpackPlugin was used to generate HTML templates, but it actually has a few other configurations:

      • Inject: Sets the injected locations of packaged resources: true, false, Body, and head

      • Cache: Set to true to generate new files only if the files are changed (the default is also true)

      • Minify: A plugin html-minifier-terser is used by default

      new HtmlWebpackPlugin({
        template: "./index.html".// inject: "body"
        cache: true.// If no changes have been made to the file, the previous cache is used
        minify: isProduction ? {
          removeComments: false.// Whether to remove the comment
          removeRedundantAttributes: false.// Whether to remove unnecessary attributes
          removeEmptyAttributes: true.// Whether to remove some empty attributes
          collapseWhitespace: false.// Collapse space
          removeStyleLinkTypeAttributes: true.// Link type="text/ CSS"
          minifyCSS: true.// Whether to compress the CSS
          minifyJS: {
            mangle: {
              toplevel: true}}} :false
      })
    Copy the code
  • InlineChunkHtmlPlugin can help to inline some chunk modules into HTML:

    • For example, the Runtime code, the code is small, but must be loaded;

    • So we can inline the HTML directly;

  • The plugin is implemented in react-dev-utils, so we can install it:

    npm install react-dev-utils -D

  • Configure in production plugins:

    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/,])

19. Customize the Loader

  • What is Loader?

    Loader is used for module source code conversion (processing), we have used a lot of Loader before, such as CSS-loader, style-loader and so on

  • How to define my own Loader?

    Loader is essentially a JavaScript module exported as a function; The Loader Runner library calls this function and passes in the results or resource queries generated by the previous Loader

  • Write a my-loader module that takes three arguments

    Content: indicates the content of the resource file

    Map: Data related to sourcemap

    Meta: Some metadata

    module.exports = function(content, map, meta) {
        return content
    }
    Copy the code
  • Importing a loader when loading a module: The path passed is related to the context

    {
        test: /\.js$/i,
        use: ["./loaders/myLoader.js"],},Copy the code
  • If we still want to load our own loader folder directly, we can configure the resolveLoader property

    resolveLoader: {
        modules: ["node_modules"."./loaders"],},Copy the code
  • Why is loader executed in reverse order?

    • Run-loader executes PitchLoader first and loaderIndex++ during PitchLoader execution.

    • NormalLoader is executed after run-loader, and loaderIndex–;

  • How do you change the order in which they are executed?

    • We can split multiple Rule objects and change their order by Using Enforce.
  • Enforce has four methods:

    • By default, all loaders are normal;

    • The loader set in the line is inline (or import ‘loader1! loader2! . / test. Js’);

    • You can also set pre and Post by using Enforce.

  • On Pitching and Normal they are executed in the following order:

    • Post, inline, normal, pre;

    • Pre, normal, inline, POST;

  • Synchronization of the Loader

    • What is a synchronous Loader?

      • The default Loader created is the synchronous Loader;

      • This Loader must return the result via a return or this.callback and pass it to the next Loader.

      • Usually we use this.callback in case of errors;

    • This. Callback can be used as follows:

      • The first argument must be Error or NULL;

      • The second argument is a string or Buffer;

      module.exports = function(content) {
        this.callback(null, content)
      }
      Copy the code
  • Asynchronous Loader

    • What is an asynchronous Loader?

      • Sometimes we use Loader to do some asynchronous operations;

      • We want to return the result of the Loader processing after the asynchronous operation is complete. At this point we will use the asynchronous Loader;

    • Loader-runner has provided methods to make the loader an asynchronous loader when executing the loader

      module.exports = function(content) {
        const callback = this.async()
        setTimeout(() = > {
          callback(null, content)
        })
      }
      Copy the code
  • Pass and get parameters

    • When using loader, pass in parameters
    • This can be done by using the official webpack parsing libraryloader-utils npm install loader-utils -D
    {
        test: /\.js$/,
        use: {
          loader: "my_loader".options: {
            name: "coder".age: 20}}},Copy the code
    const { getOptions } = require("loader-utils");
    module.exports = function (content) {
      // Get the argument passed:
      const options = getOptions(this);
      // Set the loader to asynchronous
      const callback = this.async();
      setTimeout(() = > {
        console.log("The argument passed in is :", options);
        callback(null, content);
      }, 2000);
    };
    Copy the code
  • Parameter verification: The official verification library schema-utils provided by Webpack

Parameter format:

{
  "type": "object"."properties": {
    "name": {
      "type": "string"."description": "Please enter your name"
    },
    "age": {
      "type": "number"."description": "Please enter your age"}},// Allow additional arguments to be passed in
  "additionalProperties": true
}
Copy the code
const { getOptions } = require("loader-utils");
const { validate } = require("schema-utils");
const schema = require(".. /my-schema/my-loader-schema.json");
module.exports = function (content) {
  const options = getOptions(this);
  validate(schema, options, {
    name: "my_loader"});const callback = this.async();
  setTimeout(() = > {
    callback(null, content);
  }, 2000);
};
Copy the code
  • Implement a custom babel-loader
const babel = require("@babel/core");
const { getOptions } = require("loader-utils");
module.exports = function(content) {
  // 0. Set the loader to asynchronous
  const callback = this.async();
  // 1. Get the parameters passed in
  const options = getOptions(this);
  // 2. Convert source code
  babel.transform(content, options, (err, result) = > {
    if (err) {
      callback(err);
    } else {
      callback(null, result.code)
    }
  })
}
Copy the code
  • Implement a loader that can package md files into HTML

    If you want some words to be highlighted, you can introduce CSS styles from the Highlight module into the entry function

import ".js/styles/default.css";

const marked = require('marked');
const hljs = require('highlight.js');

module.exports = function(content) {
  // Style processing
  marked.setOptions({
    highlight: function(code, lang) {
      returnhljs.highlight(lang, code).value; }})// md --> html
  const htmlContent = marked(content);
  const innerContent = "`" + htmlContent + "`";
  const moduleCode = `var code=${innerContent}; export default code; `
  return moduleCode;
} 
Copy the code

The use of 20. Tapable

  • Webpack and tapable

    We know that WebPack has two very important classes: Compiler and Compilation

    • They listen for the entire webopack lifecycle by injecting plug-ins
    • Plugins can’t be injected without a variety of hooks, and how do they get their hooks? We are actually creating instances of various hooks in the Tapable library

    So if you want to develop custom plug-ins, it’s a good idea to know about one library: Tapable

    • Table is a library that is officially written and maintained

    • Tapable manages the required hooks that can be applied to our plug-in

  • The Hook Tapable

  • Hook classification in Tapable

    Synchronous and asynchronous:

    • A Hook that begins with sync is a synchronized Hook.

    • Two events that start with async handle callbacks and do not wait for the last processing callback to finish before executing the next one;

    Other categories

    • Bail: When there is a return value, subsequent events will not be triggered.

    • Loop: The event is repeated when the return value is true, and exits when the return value is undefined or nothing is returned.

    • Waterfall: If the return value is not undefined, it will take the result returned this time as the first parameter of the next event;

    • Parallel: Parallel, the second event processing callback is executed at the same time before the next event processing callback is executed;

    • Series: serial, will wait for the last is asynchronous Hook;

  • The use of SyncHook

const { SyncHook } = require("tapable");

class LearnTapable {
  constructor() {
    this.hooks = {
      syncHook: new SyncHook(["name"."age"]),};this.hooks.syncHook.tap("event1".(name, age) = > {
      console.log("event1", name, age);
      return "event1";
    });
    this.hooks.syncHook.tap("event2".(name, age) = > {
      console.log("event2", name, age);
    });
  }
  emit() {
    this.hooks.syncHook.call("coder".18); }}const lt = new LearnTapable();
lt.emit();
/ / output:
// event1 coder 18 
// event2 coder 18
Copy the code
  • The use of SyncBailHook
const {  SyncBailHook } = require("tapable");
class LearnTapable {
  constructor() {
    this.hooks = {
      syncHook: new SyncBailHook(["name"."age"])};this.hooks.syncHook.tap("event1".(name, age) = > {
      console.log("event1", name, age);
      return "event1";
    });
    this.hooks.syncHook.tap("event2".(name, age) = > {
      console.log("event2", name, age);
    });
  }
  emit() {
    this.hooks.syncHook.call("coder".18); }}const lt = new LearnTapable();
lt.emit();
// Output: event1 coder 18
Copy the code
  • SyncLoopHook use: The above column will print continuously: event1 coder 18

  • Use SyncWaterfallHook: this column will output: event1 coder 18; event2 event1 18

  • The use of AsyncSeriesHook

const { AsyncSeriesHook } = require("tapable");
class LearnTapable {
  constructor() {
    this.hooks = {
      asyncHook: new AsyncSeriesHook(["name"."age"])};this.hooks.asyncHook.tapAsync("event1".(name, age, callback) = > {
      setTimeout(() = > {
        console.log("event1", name, age);
        callback();
      }, 2000);
    });

    this.hooks.asyncHook.tapAsync("event2".(name, age, callback) = > {
      setTimeout(() = > {
        console.log("event2", name, age);
        callback();
      }, 2000);
    });
  }
  emit() {
    this.hooks.asyncHook.callAsync("kobe".30.() = > {
      console.log("First event execution completed"); }); }}const lt = new LearnTapable();
lt.emit();
/ / output:
// event1 kobe 30
// Event2 Kobe 30(2 seconds later)
// The execution of the first event is complete
Copy the code
  • AsyncParallelHook:

    Event1 KOBE 30 event2 KOBE 30 The first event is completed

21. Custom Plugin

  • How do plugins in WebPack register into the WebPack lifecycle?

    1. Register all plug-ins in the createCompiler method of the Webpack function

    2. When the plug-in is registered, the plug-in function or the apply method of the plug-in object is called

    3. The plug-in methods receive compiler objects, which can be used to register Hook events

    4. Some plugins also pass in a compilation object, so we can listen for Hook events from that compilation

  • Develop your own plug-ins

The first step is to understand the Compiler hook portal

22. Gulp tools

What is Gulp?

A Toolkit to Automate & Enhance your workflow – A toolkit that helps you automate and enhance your workflow.

  • Gulp and Webpack

    • The core concept of Gulp is Task Runner

      You can define your own set of tasks and wait for them to be executed;

      Build flow based on file Stream;

      We can use gulp’s plug-in architecture to accomplish certain tasks;

    • The core concept of WebPack is Module Bundler

      Webpack is a modular packaging tool;

      You can use a variety of loaders to load different modules;

      You can use a variety of plug-ins to accomplish other tasks in the WebPack lifecycle;

    • Advantages and disadvantages of Gulp compared to Webpack:

      Compared with Webpack, gulp is more simple and easy to use. It is more suitable for writing some automatic tasks.

      However, gulp is not currently used for large projects (Vue, React, Angular). For example, gulp does not support modularity by default.

  • Basic use of GULP

    • First, you need to install gulp

      Global installation: NPM install -g gulp Local installation: NPM install gulp

    • Write gulpfile.js and write tasks in it

    export.foo = function(cb) {
        console.log('foo task running')
        returnn src('.. /src/js/*.js')}Copy the code
    • performnpx gulp foo
  • Create a gulp task

    • Each gulp task is an asynchronous JavaScript function

      • This function can take a callback as an argument, and the task ends when the callback function is called
      • Or return a stream, promise, Event Emitter, Child Process, or Observable
    • Tasks can be of either public or private type

      • Public tasks: Are exported from gulpfile and can be invoked using the gulp command

      • Private Tasks: Designed for internal use, usually as part of a series() or parallel() combination

      • Default task: execute command (NPX gulp)

        module.exports.default = (cb) = > {
          console.log("default task");
          cb();
        }
        Copy the code
      • Add: Before gulp4, the registration task was done through gulp.task

        gulp.task("bar".(cb) = > {
          console.log("bar");
          cb();
        })
        Copy the code
  • Task combination Series () and Parallel ()

    • Series: Serial task combination

    • Parallel: combination of parallel tasks

    • They can accept any number of task functions or combined operations

      // Serial execution of multiple tasks
      const seriesTask = series(task1, task2, task3);
      // Execute multiple tasks in parallel
      const parallelTask = parallel(task1, task2, task3);
      // combine again
      const composeTask = series(parallelTask, seriesTask);
      module.exports = {
        seriesTask,
        parallelTask,
        composeTask,
      };
      Copy the code

Results of three tasks:

  • Gulp exposes SRC () and **dest()** methods for handling files stored on the computer

    • SRC () takes the argument, reads the file from the file system and generates a Node Stream, which reads all the matching files into memory and processes them through the Stream
    • The flow generated by SRC () should return from the task (task function) and signal asynchronous completion
    • Dest () takes an output directory as an argument and produces a Node Stream through which content is exported to a file
    const jsTask = () = > {
      // Read the file from SRC and output it to the dist folder
      return src("./src/**/*.js")
        .pipe(dest("./dist"));
    };
    Copy the code
  • The main API provided by streams is the.pipe() method. What is the principle of the pipe method?

    • The PIPE method receives a Transform Streams or Writable Streams.
    • So the transformation stream or the writable stream, after it gets the data it can process it and pass it on to the next transformation stream or the writable stream
  • Convert files

    • If we want to do something with the files along the way, we can use the plugins provided to us by the community.

    • For example, if we want to convert ES6 to ES5, we can use the Babel plug-in.

    • If we want to compress and uglify the code, we can use the Uglify or Terser plug-in.

      const jsTask = () = > {
        // Read the file from SRC and output it to the dist folder
        return src("./src/**/*.js")
          .pipe(babel({ presets: ["@babel/preset-env"]}))//.pipe(uglify())
          .pipe(terser({ mangle: { toplevel: true } }))
          .pipe(dest("./dist"));
      };
    Copy the code
  • The glob file matches

    • The SRC () method takes a glob string or an array of glob strings as an argument to determine which files need to be manipulated

      • The glob or glob array must match at least one item, otherwise SRC () will report an error
    • The matching rules of globs are as follows

      • (one asterisk *) : Matches any number of characters in a string, including 0 matches *.js

      • (two asterisks *) : Matches any number of strings in multiple string matches, usually in the script/**/*.js file in the matching directory

      • (Inverse!) : [‘script/**/*.js’, ‘!scripts/vender/’]

        • The gloB is matched according to the position of each glob in the array.

        • So a negative glob in a glob array must be followed by a non-negative glob;

        • The first glob matches a set of matches, and the subsequent anti-glob removes some of those matches

  • Gulp file listener

    The watch() method in the gulp API is associated with the file System watcher.

    const jsTask = () = > {
      // Read the file from SRC and output it to the dist folder
      return src("./src/**/*.js")
        .pipe(babel({ presets: ["@babel/preset-env"]}))// .pipe(uglify())
        .pipe(terser({ mangle: { toplevel: true } }))
        .pipe(dest("./dist"));
    };
    
    watch("./src/**/*.js", jsTask);
    Copy the code

Gulp case column: Write cases to enable local services and packaging by starting Gulp

  • Packaging HTML files
const htmlTask = () = > {
  // Add a base field to read the directory in which the file is located -- dist/ XXX /xxx.html is packaged
  return src("./src/*.html", {base: "./src"})
    .pipe(htmlMin({
      collapseWhitespace: true
    }))
    .pipe(dest("./dist"))}Copy the code
  • Packaging JavaScript files
const jsTask = () = > {
  return src("./src/js/**.js", { base: "./src" })
    .pipe(babel({ presets: ["@babel/preset-env"] }))
    .pipe(terser({ mangle: { toplevel: true } }))
    .pipe(dest("./dist"));
};
Copy the code
  • Packing less files
const lessTask = () = > {
  return src("./src/css/*.less", { base: "./src" })
    .pipe(less())
    .pipe(postcss([postcssPresetEnv()]))
    .pipe(dest("./dist"));
};
Copy the code
  • HTML file resource injection
const injectHtml = () = > {
  return src("./dist/*.html")
    .pipe(
      inject(src(["./dist/js/*.js"."./dist/css/*.css"]) {relative: true })
    )
    .pipe(dest("./dist"));
};
Copy the code
  • Delete the generated directory
const clean = () = > {
  return del(["dist"]);
};
Copy the code
  • Setting up a local Server
const bs = browserSync.create();
const serve = () = > {
  watch("./src/*.html", series(htmlTask, injectHtml));
  watch("./src/js/*.js", series(jsTask, injectHtml));
  watch("./src/css/*.less", series(jsTask, lessTask));

  bs.init({
    port: 8080.open: true.files: "./dist/*".server: {
      baseDir: "./dist",}}); };Copy the code
  • Creating a packaging Task
const buildTask = series(clean, parallel(htmlTask, jsTask, lessTask), injectHtml);
Copy the code
  • Creating a development Task
const serveTask = series(buildTask, serve);
Copy the code

All codes:

const { src, dest, watch, series, parallel } = require("gulp");

const htmlMin = require("gulp-htmlmin");
const babel = require("gulp-babel");
const terser = require("gulp-terser");
const less = require("gulp-less"); // less
const postcss = require("gulp-postcss"); // postcss
const postcssPresetEnv = require("postcss-preset-env");
const inject = require("gulp-inject");

const browserSync = require("browser-sync");

const del = require("del");

const htmlTask = () = > {
  return src("./src/*.html", { base: "./src" })
    .pipe(
      htmlMin({
        collapseWhitespace: true,
      })
    )
    .pipe(dest("./dist"));
};

const jsTask = () = > {
  return src("./src/js/**.js", { base: "./src" })
    .pipe(babel({ presets: ["@babel/preset-env"] }))
    .pipe(terser({ mangle: { toplevel: true } }))
    .pipe(dest("./dist"));
};

const lessTask = () = > {
  return src("./src/css/*.less", { base: "./src" })
    .pipe(less())
    .pipe(postcss([postcssPresetEnv()]))
    .pipe(dest("./dist"));
};

const injectHtml = () = > {
  return src("./dist/*.html")
    .pipe(
      inject(src(["./dist/js/*.js"."./dist/css/*.css"]) {relative: true })
    )
    .pipe(dest("./dist"));
};

// Set up the local server
const bs = browserSync.create();
const serve = () = > {
  watch("./src/*.html", series(htmlTask, injectHtml));
  watch("./src/js/*.js", series(jsTask, injectHtml));
  watch("./src/css/*.less", series(jsTask, lessTask));

  bs.init({
    port: 8080.open: true.files: "./dist/*".server: {
      baseDir: "./dist",}}); };const clean = () = > {
  return del(["dist"]);
};

const buildTask = series(
  clean,
  parallel(htmlTask, jsTask, lessTask),
  injectHtml
);
const serveTask = series(buildTask, serve);

module.exports = {
  serveTask,
  buildTask,
};
Copy the code

package.json

{
  "name": "gulp_demo"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "serve": "gulp serveTask"."build": "gulp buildTask"
  },
  "author": ""."license": "ISC"."devDependencies": {
    "@babel/core": "^ 7.13.10"."@babel/preset-env": "^ 7.13.10"."browser-sync": "^ 2.26.14"."del": "^ 6.0.0"."gulp": "^ 4.0.2." "."gulp-babel": "^ 8.0.0." "."gulp-htmlmin": "^ 5.0.1." "."gulp-inject": "^ 5.0.5." "."gulp-less": "^ 4.0.1." "."gulp-postcss": "^ 9.0.0"."gulp-terser": "^ 2.0.1." "."postcss": "^ 8.2.8"."postcss-preset-env": "^ 6.7.0"}}Copy the code

23. A rollup tools

Let’s take a look at the official definition of rollup:

  • Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application.

    Rollup is a modular JavaScript packaging tool that helps you compile small code into large, complex code, such as a library or an application;

  • Rollup is defined and positioned very similar to WebPack:

    • Rollup is also a modular packaging tool, but Rollup is primarily packaged for ES Modules;
    • In addition, Webpack can usually handle various files and their dependencies through various loaders;
    • Rollup is more focused on JavaScript code (CSS, font, Vue, etc.);
    • In addition, rollup configuration and concept compared to WebPack, more concise and easy to understand;
    • In the early days of WebPack, when tree shaking was not supported, rollup had the advantage;
  • Where are Webpack and Rollup currently used?

    • Webpack is often used during actual project development (vue, React, Angular projects are all webPack-based);
    • When packaging library files, we usually use rollup (for example, vue, React, dayJS source code is itself based on rollup);
  • Basic use of rollup

Rollup: global install: NPM install rollup -g Local install: NPM install rollup -d

2. Create main.js and package it into bundle.js

NPX rollup./ SRC /main.js -f ife -o./dist/bundle.js

/ SRC /main.js -f AMD-o./dist/bundle.js

NPX rollup./ SRC /main.js -f js -o./dist/bundle.js

NPX rollup./ SRC /main.js -f umd –name xxxx-o./dist/bundle.js

  • Rollup configuration file

We can write the configuration file to rollup.config.js during development

export default {
  input: "./src/main.js".output: {
    format: "cjs".file: "dist/util.common.js",}};Copy the code

We can package the files separately, packaging more library files (users can import as needed)

  output: [
    {
      format: "umd".name: "myUtils".file: "dist/util.umd.js"}, {format: "cjs".file: "dist/util.commonjs.js"}, {format: "amd".file: "dist/util.amd.js"}, {format: "es".file: "dist/util.es.js"}, {format: "iife".name: "myUtils".file: "dist/util.browser.js"],},Copy the code
  • Resolve commonJS and third-party library issues

    • Install the library that addresses common.js:npm install @rollup/plugin-commonjs -D
    • Install the library that addresses node_modules:npm install @rollup/plugin-node-resolve -D
    • Pack to exclude Lodash
    • Babel conversion code: If we want to convert ES6 to ES5 code, we can use Babel in rollup.

    NPM install@rollup /plugin-babel -d Also needs to configure babel.connfig.js

    • NPM install rollup-plugin-terser -d

    • NPM install rollup-plugin-postcss postcss-d

    • NPM install [email protected] vue-template-compiler-d (by default the ve2.X version is installed, so the rollup-plugin-vue version is specified here)

    • Error: Process is not defined because process.env.node_env is used in our packaged vue code, so we can use a plug-in rollup-plugin-replace to set its value: npm install rollup-plugin-replace -D

    • Setting up a local Server

      • Step 1: set up the service using rollup-plugin-serve
      • Step 2: Refresh the browser automatically when the file changes
      • Step 3: On startup, enable the file listening commandnpx rollup -c -w
    • Differentiate the development environment

      "scripts": {
        "build": "rollup -c --environment NODE_ENV:production"."serve": "rollup -c --environment NODE_ENV:development -w"
      }
Copy the code

rollup.config.js

    import commonjs from "@rollup/plugin-commonjs";
    import resolve from "@rollup/plugin-node-resolve";
    import babel from "@rollup/plugin-babel";
    import { terser } from "rollup-plugin-terser";
    import postcss from "rollup-plugin-postcss";
    import vue from "rollup-plugin-vue";
    import replace from "rollup-plugin-replace";
    import serve from "rollup-plugin-serve";
    import livereload from "rollup-plugin-livereload";
    const isProduction = process.env.NODE_ENV === "production";
    const plugins = [
      // Fix common.js
      commonjs(),
      // Fix node_modules
      resolve(),
      // Handle the process problem
      replace({
        "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
      }),
      babel({
        babelHelpers: "bundled",}).// Process CSS files
      postcss(),
      vue(),
    ];
    if (isProduction) {
      // Terser code compression
      plugins.push(terser());
    } else {
      const devPlugins = [
        serve({
          // open: true, // Whether to open the browser
          port: 8080.// Which port to listen on
          contentBase: ".".// Service which folder}), livereload(), ]; plugins.push(... devPlugins); }export default {
      input: "./src/main.js".output: {
        format: "umd".name: "utils".file: "dist/util.umd.js".globals: {
          lodash: "_",}},// Pack to exclude lodash
      external: ["lodash"],
      plugins
    };
Copy the code

24. Vite tools

  • What is a Vite?

    Official positioning: next generation front-end development and build tools;

  • How do you define the next generation of development and build tools?

    We know that in real development, we often write code that is not directly recognized by browsers, such as ES6, TypeScript, Vue files, etc.

    So we have to build tools to convert and compile code, such as WebPack, rollup, and Parcel;

    But as the project gets bigger, the amount of JavaScript you need to process grows exponentially, and there are more modules;

    The build tool takes a long time to start the server and the HMR takes a few seconds to show up in the browser; So there is also such a saying: the world is bitter webpack for a long time;

  • Vite (French for “fast”, pronounced /vit/) is a new front-end build tool that dramatically improves the front-end development experience.

  • Vite structure: It is mainly composed of two parts:

    • A development server, which provides rich built-in functionality based on native ES modules, HMR is very fast;

    • A set of build instructions that use rollup to open our code and that are pre-configured to output optimized static resources for the build environment;

  • Install vite: NPM install vite -g

  • Start the project from vite: NPX vite

  • Vite support for CSS

    • Vite supports CSS processing directly: import CSS directly
    • Vite can directly support CSS preprocessing, such as less: import less directly, then install the LESS compiler
  • Vite supports postCSS conversion. You only need to install postCSS and configure the postcss.config.js configuration file

  • Vite supports TypeScript natively and uses ESBuild directly to compile:

    Just import it directly; If we look at the request in the browser, we’ll see that it’s still ts code: This is because the server Connect in Vite forwards our request; Get ts compiled code, returned to the browser, the browser can directly parse;

    • Note: On Vite2, Koa is no longer used, but a server portal built with Connect
  • Vite support for VUE

    Vue 3 single file component support: @vitejs/plugin-vue

    Vue 3 JSX support: @vitejs/plugin-vue-jsx

    Vue 2 supports underfin/ viet-plugin-vue2

  • NPM install vite-plugin-vue2 -d

    • invue.config.jsConfigure plug-ins in
      import { createVuePlugin } from "vite-plugin-vue2"
      module.exports = {
        plugins: [
          createVuePlugin()
        ]
      }
      Copy the code
  • Vite support for React

    .jsx and.tsx files are also out of the box, and they are also compiled using ESBuild:

    So we just need to write react code directly;

    Note: when loading main.js in index.html, we need to change the suffix of main.js to main.jsx as the suffix

  • Vite package project

    You can package the current project directly through NPX Vite build

    You can start a local service to preview the package using preview: NPX Vite Preview

  • Vite scaffolding tools

    In development, we can not use Vite to build all the projects from zero, such as a React project, Vue project;

    At this time, Vite also provided us with the corresponding scaffolding tools;

  • So Vite actually has two tools:

    Vite: a component tool similar to WebPack and rollup.

    @vitejs/create-app: similar to vue-cli and create-react-app.

  • What about using scaffolding tools? : NPM init @ vitejs/app

NPM install @vitejs/create-app -g create-app

  • ESBuild features:

    Super fast build times and no caching required;

    Support ES6 and CommonJS modularity;

    Tree Shaking for ES6;

    Support Go, JavaScript API;

    Support TypeScript, JSX and other syntax compilation;

    Support SourceMap;

    Support code compression;

    Support extension of other plug-ins

  • Why is ESBuild so fast?

    Written in the Go language, it can be directly converted to machine code without bytecode;

    ESBuild takes full advantage of the CPU’s multiple cores and runs them as full as possible;

    Everything in ESBuild is written from scratch rather than using a third party so you can consider performance issues from the start