The installation

NPM init -y: install webpack and webpack-CLI at the same time.

npm install webpack@4.43. 0 webpack-cli@3.312. -D
Copy the code

A global installation of WebPack is not recommended because it locks the version, and multiple projects may depend on different versions of WebPack, so it is recommended to install webPack within a project.

You can create a.npmrc file under the root directory to specify the source address on which the current project installation depends

registry=https://registry.npm.taobao.org
Copy the code

Zero configuration for Webpack

One of the gimmick advertised by WebPack is the zero-configuration build, which means that you can package it without writing any configuration, which is executable, but it doesn’t really meet your actual development needs.

For a quick run, create a new SRC /index.js file. We only write one line in index.js

console.log("hello, webpack !")
Copy the code

Then perform

npx webpack -v
Copy the code

NPX finds and starts the Webpack under the current project. You can see the dist/main.js file generated in the root directory.

You are advised to add build commands to package.json files to facilitate the addition of build parameters and make it easier to read.

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

I only wrote one line of code, but there are so many characters in the package that if you look at the progress bar below, there are many more to come.

Because WebPack has a lot of compatibility by default, it also has a lot of redundant code compared to other build tools.

Webpack5 has some optimizations, but as more and more modules change, nothing much changes.

Build information

What is the output at build time

The contents of the packaged file are many, but the main structure is very simple, in fact, a self-executing function, as follows

(function(modules) {
// todo({})"./src/login.js": (function(module.exports) {
    eval("xxxxx")})})Copy the code

In this structure, you can see that the argument to the self-executing function is an object, and the value of each key of the object contains a chunk, which is the code fragment.

It can also contain chunks, known as chunks, groups of chunks, and also known as chunk Names.

This entire object can contain multiple keys: values and is called a dependency graph.

The resource files generated after the build are called bundle files.

Several follow the following relationship:

  • abundleCorrespond to at least onechunks
  • achunksCorrespond to at least onemodule
  • amoduleCorrespond to at least onechunk

In a word:

Module, chunk, and bundle are the same logical code with three names in different transformation scenarios:

We write modules directly, webpack processes chunks, and finally generate bundles that the browser can run directly.

Configuration file for WebPack

If no configuration file is added, its default configuration is followed, which is called zero configuration. If a configuration file is added, WebPack is packaged according to the configuration file.

Usually the name of the webpack configuration file is webpack.config.js. You can configure it to something else, but it’s not necessary.

Modify package.json file by creating a command in script

–config Specifies the configuration file

 "warbler": "webpack --config ./warbler.config.js"
Copy the code

Since the content of the configuration file is very large, and the syntax is difficult to remember all at once, we can use a few tricks to make the configuration file have syntax hints.

Start by installing @types/webpack.

npm i -D @types/webpack
Copy the code

Then specify the type in the configuration file.

// webpack.config.js
/ * * *@type {import('webpack').Configuration}* /
module.exports = {
}
Copy the code

Babel.config.js supports the same approach.

npm i -D @types/babel__core
/ * * *@type {import('@babel/core').TransformOptions}* /
Copy the code

Vue.config.js is also available, and no additional packages need to be installed.

/ * * *@type {import('@vue/cli-service').ProjectOptions}* /
Copy the code

Webpack configuration parameters

webpackIs based onnodejsAll of themnodejsCore modularapiCan be directly introduced to use.

entry

The default entry for performing a packing task is SRC /index. Relative and absolute paths are supported.

entry: "./src/index.js"
Copy the code

Support string, arR, object if passed in as a string, it will also be converted to an object structure during build, the above code and the following code result is the same.

  entry: {
    main: "./src/index.js"
  },
Copy the code

Support SPA (single entrance) and MPA (multiple entrance). Multiple entries output multiple bundles.

  entry: {
    index: "./src/index.js".login: "./src/login.js".home: "./src/home.js"
  },
Copy the code

output

Output information about the resource file, including storage location and file name

  • path: Storage location, where to put packaged files default isdistThe requirement is absolute path;
  • filename: File name, what is the name of the packaged file?
  output: {
    path: path.resolve(__dirname, './dist'), 
    filename: "main.js"
  },
Copy the code

However, if entry sets multiple entries, multiple main.js will be generated and an error will be reported.

The solution is to use placeholders with the following syntax

filename: "[name].js"
Copy the code

The function is that the key in the entry is named whatever the filename is after being packed. Multiple entrances will correspond to multiple exits.

mode

Packaging mode, default is production mode, production mode will do code compression, tree shaking and other operations.

  • developmentDevelopment mode
  • productionProduction mode
  • noneThe default

plugins

A plug-in. A plug-in is essentially a class. The configuration of various common plug-ins will be explained separately later.

Plugins are executed from top to bottom.

plugins:[]
Copy the code

module

Module, webpack default only support.js,.json files, like we usually commonly used.vue,.png,.scss,.css,.ts and so on are not supported.

So if you want WebPack to support other types of files, you need a different type of Loader to parse.

The following configuration means what loader is used to process.css files when testing.

// webpack.config.js
 module: {
    rules: [{test: /\.css$/,
        use: ["style-loader"."css-loader"]},]}Copy the code
  • css-loader:cssModule serialization, letwebpackknowcssSyntax can not be generatedchunk.
  • style-loader:cssIntegration inhtmlOf the fileheadOf the labelstyleIn the.

When multiple Loaders are applied to a module, they are executed from right to left. That is, csS-loader is executed before style-loader is executed.

In fact, a Loader can also handle the above two tasks, but it is recommended that a Loader only do one thing, that is, follow the principle of single responsibility.

You can also write the loader configuration, in which case the object structure is used.

// webpack.config.js
 module: {
    rules: [{test: /\.less$/,
        use: [
          {
            loader: 'style-loader'}, {loader: "css-loader".options: {
              modules: true}}, {loader: 'postcss-loader'}, {loader: 'less-loader',}]}]}Copy the code
  • less-loader:lessSyntactic conversion tocssSyntax.
  • postcss-loaderpostcssIs aTool setThis is very powerful,postcsscssIs equivalent tobabelforjsAs a plug-in itself, it can also carry plug-ins.

resolveLoader

Parse the loader and tell WebPack how to match the Loader. When we customize some loaders. We can use the absolute path to import the file, but it is too tedious. When we specify the folder through this field, we can write the loader name as if using a third-party loader.

// webpack.config.js
 resolveLoader: {
    // The default is node_modules
    modules: ["node_modules"."./myLoaders"]},Copy the code

Common configurations of common plug-ins

html-webpack-plugin

Version: 4.5.2

  • template: Generated using a specified templatehtml
  • filename: PackedhtmlThe file name.
  • chunks: PackedhtmlWhat will be includedchunk.
// webpack.config.js
  plugins: [
    // Automatically generate HTML files, import bundle files, compress HTML
    new htmlWebpackPlugin({
      // The template matches
      template: "./src/index.html".filename: "index.html".chunks: ["index".'login'],}),]Copy the code

If you want to generate multiple HTML, you simply create multiple instances.

// webpack.config.js
plugins: [
    // Automatically generate HTML files, import bundle files, compress HTML
    new htmlWebpackPlugin({
      // The template matches
      template: "./src/index.html".filename: "index.html".chunks: ["index".'login'],}).// Automatically generate HTML files, import bundle files, compress HTML
    new htmlWebpackPlugin({
      // The template matches
      template: "./src/index.html".filename: "login.html".chunks: ["login"],}).// Automatically generate HTML files, import bundle files, compress HTML
    new htmlWebpackPlugin({
      // The template matches
      template: "./src/index.html".filename: "home.html".chunks: ["home"],}),]Copy the code

But as a lazy programmer, you would never write this code, because the WebPack configuration file is itself an object. So of course there are ways to generate configuration items automatically.

In this example, each folder contains one index.html and one index.js. The structure is as follows. Of course, you can define the structure at will, and then write the corresponding function body.

├ ─ ─ the SRC ├ ─ ─ the list │ ├ ─ ─ index. The HTML │ └ ─ ─ index. The js ├ ─ ─ the login │ ├ ─ ─ index. The HTML │ └ ─ ─ index. The js ├ ─ ─ the detail │ ├ ─ ─ index. The HTML │ └ ─ ─ index. Js ├ ─ ─ index │ ├ ─ ─ index. The HTML │ └ ─ ─ index, jsCopy the code

The configuration is as follows, using the setMap method to automatically generate entry and htmlWebpackPlugins so that we don’t have to modify the configuration every time we add a file.

// webpack.config.js
const path = require('path')
const htmlWebpackPlugin = require("html-webpack-plugin")
// Fuzzy match path
const glob = require('glob')

// Automatically generate entry and htmlWebpackPlugins
const setMap = () = > {
  const entry = {};
  const htmlWebpackPlugins = []
  // Fuzzy-match index.js in any directory under SRC returns the absolute path to the file
  const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"))
  // Iterate over the matched result
  entryFiles.forEach((entryFile) = > {
    // Get the file name
    const pageName = entryFile.match(/src\/(.*)\/index\.js$/) [1]
    / / generated entry
    entry[pageName] = entryFile
    / / generated htmlWebpackPlugins
    htmlWebpackPlugins.push(
      new htmlWebpackPlugin({
        template: `./src/${pageName}/index.html`.filename: `${pageName}.html`.chunks: [pageName]
      }))
  })

  return {
    entry,
    htmlWebpackPlugins
  }
}

const { entry, htmlWebpackPlugins } = setMap()

// Config file
module.exports = {
  entry,
  // Output information about the resource file
  output: {
    // Storage location
    path: path.resolve(__dirname, './dist'),
    // File name
    filename: " [name].js"
  },
  // Package mode
  mode: "development"./ / the plugin
  plugins: [
    ...htmlWebpackPlugins,
  ],
}

Copy the code

postcss

Postcss also has its own configuration file, postcss.config.js, which is also a module, installed via require(“autoprefixer”).

// postcss.config.js
module.exports = {
  plugins: [
    require("autoprefixer"),require("cssnano")]}Copy the code

Postcss plugin for Autoprefixer

Autoprefixer automatically prefixes CSS properties using can I use data.

But that doesn’t work yet, so you need to set up Browserslist, the target set of browsers, the compatible version of the browser, and the tool that uses the browserslist will specifically output the compatible code based on browserslist’s description.

Browserslist can be set up in two ways.

You can add properties directly to the package.json file as follows

  "browserslist": [
    "1%" >."last 2 versions"
  ]
Copy the code

You can also create a.browserslistrc file in the root directory and write it directly, without parentheses.

// .browserslistrc
>1%
last 2 versions
Copy the code
  • >1% : browsers with a global market share of more than 1%.
  • Last 2 Versions: indicates the latest two major versions of compatible browsers.

You can use NPX Browserslist to query for compatible browser versions.

Postcss plugin CSsnano

Compress the CSS code.

mini-css-extract-plugin

Pull out the CSS code into a separate file, you can specify the filename, support absolute path, will automatically generate folders. Replace style-loader with minicss.loader where loader is written.

// webpack.config.js
const minicss = require("mini-css-extract-plugin")
module.exports = {
  plugins: [
    new minicss({
      filename: 'style/index.css'})].module: {
    rules: [{test: /\.less$/,
        use: [
          minicss.loader,
          {
            loader: "css-loader".options: {
              modules: true}}, {loader: 'postcss-loader'}, {loader: 'less-loader',}]}]}Copy the code

clean-webpack-plugin

Clean up the packing directory before each packing.

// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
  plugins: [
     newCleanWebpackPlugin ()],}Copy the code

Implement a custom Loader

The loader reference

Create a myLoaders/a-loader.js file in the root directory and import it in the Module using an absolute path.

// webpack.config.js
module: {
    rules: [{test: /\.js$/,
        use: [
         {
            loader: path.resolve(__dirname, './myLoaders/a-loader.js'),},]},]}Copy the code

The structure of the loader

Loader is a function. Take source, which is the content of the module that matches the test re, in this case the.js file.

  • This function cannot beArrow functionBecause thewebpackprovidesloader apiIt’s all mounted tothisOn the.
  • This function must return a value or an error will be reported.
// a-loader.js
module.exports = function(source) {
  console.log('🚀 🚀 ~ source:', source);
  return source
}
Copy the code

The SRC /index.js file outputs only one line of logs.

// src/index.js
console.log('hello, world');
Copy the code

Source is the SRC /index.js file that has not been compiled by Webpack.

The writing of the loader

In a-loader.js, we replace hello with hello and return.

// a-loader.js
module.exports = function(source) {
  const _source = source.replace('hello'.'hello' )
  return _source
}
Copy the code

If you look at the chunk, console.log(‘ Hello world’) shows that our custom loader has taken effect.

When we use a third-party loader, we usually define a configuration item options, so how to set the options of our custom loader?

First, set an options property in Use like any other loader.

// webpack.config.js
module: {
    rules: [{test: /\.js$/,
        use: [
         {
            loader: path.resolve(__dirname, './myLoaders/a-loader.js'),
            options: {
              name: "A Warbler's Tail"}},]},]}Copy the code

This. Query is used to retrieve options from a custom loader, which explains why loader cannot be an arrow function.

Replace world with name in the options we set.

// a-loader.js
module.exports = function(source) {
  const _source = source.replace('world'.this.query.name )
  return _source
}
Copy the code

Ok, successful substitution.

It is common for loader to perform asynchronous operations, so we need to use this.callback, a function that can be called synchronously or asynchronously and return multiple results.

this.callback(
  err: Error | null.content: string | Buffer, sourceMap? : SourceMap, meta? : any );Copy the code
  • The first parameter must beErrorornull
  • The second argument is astringorBuffer.
  • Optional: The third argument must be one that can be parsed by the modulesource map.
  • Optional: The fourth option, will bewebpackIgnore, which can be anything (such as some metadata).

We also need to use this.async to tell the loader’s parser that the loader will call back asynchronously.

Create a new myLoaders/b-loader.js file and use setTimeout to simulate an asynchronous operation.

// b-loader.js
module.exports = function(source) {
  const _callback = this.async();
  const _source = source.replace('world'.this.query.name)
  setTimeout(() = > {
    _callback(null, _source)
  }, 3000)}Copy the code

Then reference the Loader.

// webpack.config.js
module: {
    rules: [{test: /\.js$/,
        use: [
         {
            loader: path.resolve(__dirname, './myLoaders/b-loader.js'),
            options: {
              name: "A Warbler's Tail"}},]},]}Copy the code

If you look at the build time, it’s over three seconds.

And that’s what we wanted, replacing world with my name a warbler.

Asynchronous Loaders do not affect the loaders of other modules, but affect the loaders that multiple Loaders work on one module.

When a loader is finished, it will be handed over to the next loader until there is no loader, and finally to Webpack.

Let’s test the case that two Loaders work on the same module. Since loader runs from right to left, we write B-Loader first and a-loader later.

// webpack.config.js
module: {
    rules: [{test: /\.js$/,
        use: [
          {
            loader: path.resolve(__dirname, './myLoaders/b-loader.js'),
            options: {
              name: "A Warbler's Tail"}}, {loader: path.resolve(__dirname, './myLoaders/a-loader.js'),},]},]}// b-loader.js
module.exports = function(source) {
  const _callback = this.async();
  const _source = source.replace('world'.this.query.name)
  setTimeout(() = > {
    _callback(null, _source)
  }, 3000)}// a-loader.js
module.exports = function(source) {
  const _source = source.replace('hello'.'hello')
  return _source
}
Copy the code

As you can see, both loaders are in effect.

However, there is a problem that our custom loader uses the absolute path reference mode, which is too tedious and cannot be read.

The solution is the resolveLoader configuration parameter mentioned above, which allows you to write the loader name as if you were using a third-party loader.

// webpack.config.js
 resolveLoader: {
    // The default is node_modules
    modules: ["node_modules"."./myLoaders"]},module: {
    rules: [{test: /\.js$/,
        use: [
          {
            loader: "b-loader".options: {
              name: "A Warbler's Tail"}}, {loader: "a-loader",},]},]}Copy the code

Practice writing loader

my-style-loader

module.exports = function(source) {
  return `
    const tag = document.createElement("style")
    tag.innerHTML = ${source}
    document.head.appendChild(tag)
  `
}
Copy the code

my-css-loader

module.exports = function(source) {
  return JSON.stringify(source)
}
Copy the code

my-less-loader

const less = require("less")
module.exports = function(source) {
  less.render(source, (error, output) = > {
    const cssInfo = output.css
    this.callback(error, cssInfo)
  })
}
Copy the code

Working with static Resources

As mentioned above, webpack does not recognize file types other than.js and.json, so we need to use different loaders to handle static resources.

Next we will learn about the new loader to handle image resources.

file-loader

Function: Exports a resource and returns the path.

Options:

  • name: Image name, which can be a placeholder.[name]The name,[ext]The suffix.
  • outputPath: The storage location of the output resourcedistDirectory.
  • publicPath: Where resources are used,publicPath + name = The full usage path of images in the CSS.
// webpack.config.js
  module: {
    rules: [{test: /\.(jpe? g|png|gif|webp)$/,
        use: [
          {
            loader: "file-loader".options: {
              name: "[name].[ext]".outputPath: "images".publicPath: ".. /images",}},]},]}Copy the code

url-loader

Function: Rely on file-loader, transfer the image to base64 encoding, can reduce the image request.

Options:

  • name: Image name, which can be a placeholder.[name]The name,[ext]The suffix.
  • outputPath: The storage location of the output resourcedistDirectory.
  • publicPath: Where resources are used,publicPath + name = The full usage path of images in the CSS.
  • limit: Images larger than the specified size will not be converted tobase64, in bytes.
// webpack.config.js
  module: {
    rules: [{test: /\.(jpe? g|png|gif|webp)$/,
        use: [
          {
            loader: "url-loader".options: {
              name: "[name].[ext]".outputPath: "images".publicPath: ".. /images".limit: 4 * 102,}},]},]}Copy the code

image-webpack-loader

This loader needs to use CNPM to install successfully.

cnpm i image-webpack-loader -D
Copy the code

Purpose: Image compression

// webpack.config.js
  module: {
    rules: [{test: /\.(jpe? g|png|gif|webp)$/,
        use: [
          {
            loader: 'image-webpack-loader',}]},]}Copy the code

If you look at the source file compared to the output file, the effect is still remarkable.

Image processing in webpack5

// webpack.config.js
  module: {
    rules: [{test: /\.(jpe? g|png|gif|webp)$/.// The asset generic configuration defaults to an 8KB limit over use asset/resource otherwise use asset/inline
        // asset/resource is equivalent to file-loader
        // asset/inline is the same as url-loader
        type: "asset/resource".// The generator property can only be set in asset/resource
        generator: {
          filename: "images/[name][ext]",}parser: {
          dataUrlCondition: {
            The maxSize attribute takes effect only if you set it to asset
            maxSize: 1 * 1024}}}]}Copy the code

Font file processing

The font file is referenced like this.

// index.less
@font-face {
  font-family: 'webfont';
  font-display: swap;
  src: url('.. /font/webfont.eot'); /* IE9 */
  src: url('.. /font/webfont.eot? #iefix') format('embedded-opentype'),
    /* IE6-IE8 */ url('.. /font/webfont.woff2') format('woff2'),
    url('.. /font/webfont.woff') format('woff'), /* Chrome, Firefox */url('.. /font/webfont.ttf') format('truetype'/* Chrome, Firefox, Opera, Safari, Android, iOS4.2+ * /url('.. /font/webfont.svg#webfont') format('svg'); / * iOS 4.1 - * /
}
body {
  div {
    height: 100px;
    background: blue;
    display: flex;
    background: url(../images/main.jpg) 0 0 no-repeat;
    font-family: 'webfont' ! important; }}Copy the code

Font file processing and image is the same, using the same loader.

Font files can also be converted to base64 format, so urL-loader can also be used.

// webpack.config.js
  module: {
    rules: [{test: /\.(eot|svg|ttf|woff|woff2)$/,
        use: [
          {
            loader: "file-loader".options: {
              name: "[name].[ext]".outputPath: "font".publicPath: ".. /font",}},]},]}Copy the code

Working with JavaScript modules

babel

First, install both of the following: babel-loader depends on @babel-core.

npm i @babel-core babel-loader -D
Copy the code

Much like PostCSS, Babel is a toolset that doesn’t do anything by itself, relying on its own plug-ins.

Babel is also a tool that works with collections of browsers.

@babel/preset-env

Used to deal with compatibility issues with ES6 + syntax.

Let’s start with something new for ES6.

// index.js
const arr = [new Promise(() = >{})]Copy the code

Then configure the plug-in.

// webpack.config.js
  module: {
    rules: [{test: /\.js$/,
        use: [
          {
            loader: 'babel-loader'.options: {
              presets: ["@babel/preset-env"]}}]},]}Copy the code

Pack and see what happens.

As you can see, const has been converted to VAR, but promise has not been converted. As mentioned above, this plugin is only used to transform ES6 + syntax, not new features.

So what to do? Of course, you have to rely on other friends.

@babel/polyfill

This package is used at run time, so it is installed into the production dependency.

 npm i @babel/polyfill -S
Copy the code

This package contains all the new features to be referenced before any code.

// index.js
import "@babel/polyfill"
const arr = [new Promise(() = >{})]Copy the code

In later versions of babel7.4, the new approach is recommended.

// index.js
import "core-js/stable"; 
import "regenerator-runtime/runtime";
const arr = [new Promise(() = >{})]Copy the code

But since the package is too large, we also need to configure on-demand imports, again via @babel/preset-env options.

// webpack.config.js
  module: {
    rules: [{test: /\.js$/,
        use: [
          {
            loader: 'babel-loader'.options: {
              presets: [["@babel/preset-env", {
                  // Specify a collection of browsers separately
                  targets: {},
                  NPM I core-js@3 -s is required for the core-js version
                  corejs: 3./** * false When we introduce polyfill, we will not import on demand, the bundle will be very large * entry needs to import import in the entry file"@babel/polyfill" Babel will help us to import * usage on demand */
                  useBuiltIns: "usage"}]]}}]},]}Copy the code

Babel can also have its own configuration file. Create a new babel.config.js file in the root directory, and move the contents of the options file.

// babel.config.js
module.exports = {
  presets: [["@babel/preset-env", {
      targets: {},
      corejs: 3.useBuiltIns: "usage"}}]]Copy the code

Integrated the react

Integrate react and install dependencies.

npm i react react-dom -S 
npm i @babel/preset-react -D
Copy the code

Configure the Babel. Config. Js.

// babel.config.js
module.exports = {
  presets: [["@babel/preset-react"}, {}]]Copy the code

Integrated vue

Integrate vUE, install dependencies. This has nothing to do with Babel, by the way.

Note that the vUE and vue-template-Compiler versions must be the same.

npm i vue vue-loader vue-template-compiler -D
Copy the code

Configuration webpack. Config. Js.

// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  module: {
    rules: [
      / /... Other rules
      {
        test: /.vue$/,
        loader: 'vue-loader'}},plugins: [
    // Be sure to introduce this plugin!
    new VueLoaderPlugin()
  ]
}
Copy the code

Implement custom plugins

The structure of the plugin

Create myPlugins/my-plugin.js in the root directory.

The essence of a plugin is a class. Receive the options configuration through the constructor. Each plugin must have a built-in Apply method, which receives a parameter Compiler, the instantiated Webpack, containing configuration information and so on.

// my-plugin.js
class MyPlugin {
  constructor(options) {
    console.log('🚀 🚀 ~ options:', options);
  }
  apply(compiler) {
    / / the console. The log (' 🚀 🚀 ~ : my - plugin ');}}module.exports = MyPlugin
Copy the code

The plugin’s reference

You can pass options when creating an instance.

// webpack.config.js
const MyPlugin = require("./myPlugins/my-plugin")
module.exports = {
  plugins: [
    new MyPlugin({
      name: A Warbler's Tail}})]Copy the code

The writing of the plugin

How to determine the timing of plugin execution?

Webpack also has lifecycle hooks. We can take a look at what the hooks are.

// Create webpack.js in the directory and start it from Node webpack.js
/ / introduce webpack
const webpack = require("webpack")

// Import the configuration file
const config = require("./webpack.config.js")

// Produce the webPack instance from the configuration file
const compiler = webpack(config)

// Prints the webPack lifecycle hooks
Object.keys(compiler.hooks).forEach((hookName) = > {
  compiler.hooks[hookName].tap("xxx".(compilation) = > {
    console.log('🚀 🚀 ~ hookName:', hookName); })})// Perform compilation
compiler.run()
Copy the code

All of the lifecycle hooks are on the Compiler. hooks property, so we iterate over the printed result as follows:

You can see that webPack has too many lifecycle hooks.

So our plugin events just need to register with the corresponding lifecycle hooks, and the Webpack will pass through our plugin at the appropriate time.

class MyPlugin {
  constructor(options){}// Accepting a parameter Compiler is that the instantiated Webpack contains configuration information
  apply(compiler) {
    // Register event synchronous hooks with TAP asynchronous hooks with tapAsync
    // The event name can be any value. It is recommended to keep the semantics consistent with the plug-in name
    compiler.hooks.emit.tapAsync('xxx'.(compilation, cb) = > {
      // compilation
      console.log(' '🚀 🚀 ~ compilation., compilation.assets);
      const content = 'hello,plugin xxxxx'
      // Add static resources
      compilation.assets['warbler.txt'] = {
        source: function() {
          return content
        },
        // It is only used for viewing and does not affect the actual file size
        size: function() {
          return content.length
        }
      }
      cb()
    })
  }
}
module.exports = MyPlugin
Copy the code

Events can be registered in any lifecycle using the Compiler parameter of the Apply method, but some lifecycle are synchronous and some are asynchronous. For details, see the official website.

Synchronous hooks register events with TAP and asynchronous hooks register events with tapAsync.

For asynchronous hooks, a CB argument is passed in the event callback and called at the end.

The event name can be any value. It is recommended to keep the same semantics as the plug-in name for easy reading of the source code and understanding of the function.

The compilation parameters for the event callbacks are the half-finished compilation parameters of the previous hook execution.