preface

Have you encountered any of these problems:

  • Webpack-packed files are not compressed
  • Static files are manually copied to the output directory
  • There is a lot of redundant code written in the code for environmental judgments

The previous “WebPack Core features” Loader talked about webPack’s Loader mechanism. This article focuses on another core feature: plugins.

The plug-in mechanism is designed to solve these problems by automating the work of a project in addition to the packaging of resource modules.

Plugins are designed to extend WebPack functionality by injecting hooks into the build process, which gives WebPack a lot of flexibility.

What are the differences between plugins and Loaders?

Loader is a converter that compiles and converts one file into another. For example, convert a.less file to a.css file.

Plugins are extenders for the entire packaging process after the loader finishes. It does not directly operate on files, but instead works based on an event mechanism. Events are broadcast at specific times in the WebPack build process, and the plug-in can listen for these events to occur and do something at specific times. These include: packaging optimization, resource management, injecting environment variables.

How should the plugin be configured?

For example, HtmlWebpackPlugin can generate an HTML file for us that includes all the modules in the body using the script tag. Here’s how to configure it:

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

const webpackConfig = {
  ...
  plugins: [new HtmlWebpackPlugin()]
};
Copy the code

Note Loader is configured in module.rules as the module parsing rule. The type is array. Each entry is an object containing properties such as test(type file), Loader, and options(parameter). Plugins are individually configured as arrays, each of which is an instance of the Plugin, and the arguments are passed in through the constructor.

What are some common plugins?

The following list of plugins is from webpack’s official website. If you are not familiar with the plugin, you can click on the name to jump to have a look and understand the specific gameplay.

The name of the describe
AggressiveSplittingPlugin Divide the original chunk into smaller chunks
BabelMinifyWebpackPlugin Use Babel-Minify for compression
BannerPlugin Add the banner at the top of each generated chunk
CommonsChunkPlugin Extract the generic module shared between chunks
CompressionWebpackPlugin A pre-prepared compressed version of the resource that uses Content-Encoding to provide access
ContextReplacementPlugin Override the inferred context of the require expression
CopyWebpackPlugin Copy individual files or entire directories to the build directory
DefinePlugin Global constants that allow configuration at compile time
DllPlugin To greatly reduce build time, subcontract DLLS
EnvironmentPlugin Shorthand for the process. Env key in DefinePlugin.
ExtractTextWebpackPlugin Extract text (CSS) from the bundle into a separate file
HotModuleReplacementPlugin Enable Hot Module Replacement – HMR
HtmlWebpackPlugin Simply create an HTML file for server access
I18nWebpackPlugin Added internationalization support for bundles
IgnorePlugin Exclude certain modules from the bundle
LimitChunkCountPlugin Sets minimum/maximum limits for chunks to fine tune and control chunks
LoaderOptionsPlugin Used to migrate from WebPack 1 to WebPack 2
MinChunkSizePlugin Ensure that the chunk size exceeds the specified limit
NoEmitOnErrorsPlugin During the output phase, a compilation error was encountered and skipped
NormalModuleReplacementPlugin Replaces the resource that matches the regular expression
NpmInstallWebpackPlugin Missing dependencies are automatically installed at development time
ProvidePlugin You don’t need to use modules via import/require
SourceMapDevToolPlugin More granular control over the Source Map
EvalSourceMapDevToolPlugin More granular control over the Eval Source map
UglifyjsWebpackPlugin You can control the version of UglifyJS in your project
ZopfliWebpackPlugin A pre-compressed version of the resource with Node-Zopfli

How do I write a plugin?

Before I talk about how to write plugins, let’s take a look at some of the fun things: tapable, Compiler, and Compilation.

tapable

Tapable is a library similar to NodeJS EventEmitter, which controls the publishing and subscription of hook functions. Of course, the hook mechanism provided by Tapable is more comprehensive, which is divided into synchronous and asynchronous two categories (asynchronous and asynchronous parallel and asynchronous serial), and the Bail/Waterfall/Loop type is derived according to the different termination conditions of event execution.

Basic use:

const { SyncHook } = require('tapable');
const hook = new SyncHook(['name']);
hook.tap('hello'.(name) = > {
  console.log(`hello ${name}`);
});
hook.tap('hi'.(name) = > {
  console.log(`hi ${name}`);
});

hook.call('july');

// hello july
// hi july
Copy the code

Summary:

The basic logic of tapable is to register the handler function corresponding to the hook through the tap method of the class instance, and then trigger the callback function through the call method.

compiler

The Compiler object contains all configuration information about the WebPack environment, including options, loaders, and plugins. Can be understood simply as webpack instances. Represents the entire WebPack startup to shutdown lifecycle.

Compile internal implementation:

class Compiler extends Tapable {
  constructor(context) {
    super(a);this.hooks = {
      / * *@type {SyncBailHook<Compilation>} * /
      shouldEmit: new SyncBailHook(["compilation"]),
      / * *@type {AsyncSeriesHook<Stats>} * /
      done: new AsyncSeriesHook(["stats"]),
      / * *@type {AsyncSeriesHook<>} * /
      additionalPass: new AsyncSeriesHook([]),
      / * *@type {AsyncSeriesHook<Compiler>} * /. }; . }Copy the code

Summary:

Compile inherits tapable and binds a hook object to the instance.

compilation

The Compilation object contains the current module resources, compiled generated resources, and changed files. Represents only a new compilation.

Implementation of Compilation:

class Compilation extends Tapable {
  /**
   * Creates an instance of Compilation.
   * @param {Compiler} compiler the compiler which created the compilation
   */
  constructor(compiler) {
    super(a);this.hooks = {
      / * *@type {SyncHook<Module>} * /
      buildModule: new SyncHook(["module"]),
      / * *@type {SyncHook<Module>} * /
      rebuildModule: new SyncHook(["module"]),
      / * *@type {SyncHook<Module, Error>} * /
      failedModule: new SyncHook(["module"."error"]),
      / * *@type {SyncHook<Module>} * /
      succeedModule: new SyncHook(["module"]),
      / * *@type {SyncHook<Dependency, string>} * /
      addEntry: new SyncHook(["entry"."name"]),
      / * *@type {SyncHook<Dependency, string, Error>} * /}; }}Copy the code

When running the WebPack development environment middleware, every time a file change is detected, a new Compilation is created to generate a new set of compilation resources. A Compilation object represents current module resources, compiled generated resources, changed files, and tracked dependencies. The Compilation object also provides a number of time-critical callbacks that plug-ins can choose to use when doing custom processing.

Warm up

Write a basic plugin:

// A JavaScript named function.
function SimplePlugin() {}

// Define a 'apply' method on the prototype of the plug-in function.
SimplePlugin.prototype.apply = function (compiler) {
  // Specify an event hook to mount to webPack itself.
  compiler.plugin("webpacksEventHook".function (
    compilation /* Handle webPack internal instance specific data. * /,
    callback
  ) {
    console.log("This is an simple plugin!!!");

    // When the functionality is complete, call the callback provided by WebPack.
    callback();
  });
};
Copy the code

After WebPack started, it did the following:

  • Execute first during the read configuration processnew SimplePlugin(), initialize a SimplePlugin and get an example.
  • In the initializationcompilerObject, and then callSimplePlugin.apply(compiler)For the plug-in instancecompilerObject.
  • The plug-in instance is obtained atcompilerObject, you can passcompiler.plugin("webpacksEventHook", function(compilation, callback){})Listen to webPack broadcast events and passcompilerObject to manipulate WebPack.

In actual combat

Let’s write a useful plug-in.

To do something when WebPack closes right away. For example, it tells you whether the package is completed or not. Or execute some script. We’ll call it AfterWebpackPlugin. The usage is as follows:

module.exports = {
  plugins: [
    // Pass the success and failure callbacks, respectively
    new AfterWebpackPlugin(
      () = > {
        // You can notify the user of a successful build and execute the release script
      },
      (err) = > {
        // Throw an exception when the build fails
        console.error(err); })]};Copy the code

The implementation here requires the following two hooks:

  • Done: Occurs when WebPack exits immediately after a successful build and output of the file.
  • Failed: Occurs when the build failed due to a build exception and WebPack exits immediately.

The implementation code is as follows:

class AfterWebpackPlugin {
  constructor(doneCb, failedCb) {
    // Pass the callback function
    this.doneCb = doneCb;
    this.failedCb = failedCb;
  }

  apply(compiler) {
    compiler.plugin("done".(res) = > {
      // Callback in webPack lifecycle 'done'
      this.doneCb(res);
    });
    compiler.plugin("failed".(err) = > {
      // Callback in webPack lifecycle 'failed'
      this.failedCb(err); }); }}module.exports = AfterWebpackPlugin;
Copy the code

Summary of plug-in development:

  • Notice in the WebPack lifecycle to find the appropriate hooks to complete the functionality.
  • Take care to understand the nuances of the various hooks in the WebPack lifecycle.

expand

Events that occur during the Output phase of WebPack and are explained below:

The event name explain
should-emit All files that need to be output have been generated. Ask the plug-in which files need to be output and which do not
emit After determining which files to output, file output is performed, where the output can be retrieved and modified
after-emit File output completed
done Complete a complete compilation and output process successfully
failed If an exception is encountered during the compilation and output process, causing webPack to exit, it will be directly skipped to this step, and the plug-in can obtain the specific error cause in this event

series

  • Webpack Core Features loader
  • “Webpack Core Features” plugin
  • “Webpack Core Features” Module Hot Replacement (HMR)

Thank you

  • If this article helped you, just give it a like! Thanks for reading.
  • I participated in the 2020 List of creators. If you like me, you can vote for me