directory

  1. The basic concept
  2. Method of use
  3. The source code to learn
  4. The development of actual combat
  5. other

1. Basic concepts

Last week I wrote webPack Loader by Hand, so of course I’m here to work on plugin.

What is a plugin?

A plugin is a JavaScript object with the Apply method.

A plugin is, in effect, a function.

It can be a normal function:

function MyPlugin(){}
Copy the code

Or special functions:

class MyPlugin{
  constructor(){}}// class is actually the syntactic sugar of the function
Copy the code

We learned loader last week and learned that Loader processes files that comply with type rules. The input and output parameters are both file streams, so Loader has limitations. For some operations unrelated to files, such as environment configuration before packaging and notification email after packaging, Loader cannot do anything.

Plugin is designed to solve loader’s limitations.

2. Usage

4 simple steps to build a WebPack project:

  1. Create and CD into the foldertest-webpack
  2. NPM init-y, initializes the project
  3. NPM install webpack webpack-cli –save-dev, install webpack dependency
  4. Add the following files:
  • webpack.config.js
  • index.html
  • src
    • index.js

Use the HtmlWebpackPlugin in your project

Taking HtmlWebpackPlugin as an example, it is used in our test-Webpack project as follows:

  1. Installation:npm i html-webpack-plugin -D
  2. Configuration webpack. Config. Js:
// test-webpack/webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js".mode: "development".output: {
    filename: "main.js".path: path.resolve(__dirname, "dist"),},plugins: [
    new HtmlWebpackPlugin({
      title: "Webpack~",})};Copy the code

The plugins array in the configuration file is where we configure the Plugin plug-in.

HtmlWebpackPlugin can automatically generate index.html file in dist folder, and automatically introduce packaged CSS and JS script files into it.

After configuration, add a script to package.json so that we can execute the packaging instructions:

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

Run NPM run build or YARN run build. You can see that the index. HTML file is automatically generated in the dist folder and the generated main.js script file is automatically imported.

So far, we’ve seen how to install and configure the plug-in to use in your project.

Next, we go to see the HtmlWebpackPlugin source code, a further understanding of plugin.

3. Source code learning

Learn how to write plugin from HtmlWebpackPlugin source code

Above is the HTML -webpack-plugin/index.js folded source code.

Expand but not completely expand class HtmlWebpackPlugin{} to see:

Plugin is a JavaScript object with the apply method.

Then expand Apply:

apply (compiler) {
  compiler.hooks.initialize.tap('HtmlWebpackPlugin'.() = > {
    // omit the XXX word here
  });
}
Copy the code

Okay, here’s the question:

  • 1) When is the apply method called?
  • 2) What is the input parameter compiler?
  • 3)compiler.hooks.initialize.tapWhat do you mean?

To find out the answers to these questions, you have to go to the webpack source code, go —

Learn plugin implementation principle from Webpack source code

Yarn Run Webpack: yarn Run Webpack: yarn Run Webpack: yarn Run Webpack

After webpack starts, the Compiler instance object Compiler [1] is created, and then the Apply method defined in plugin is called and Compiler is passed to Apply.

Here compiler is an instance object of the Compiler class; The Compiler class “extends from the Tapable class to register and invoke plug-ins.” [2]

The Compiler class provides many hook functions. We can see which hook functions are defined in compiler.js:

When Plugin. apply(Compiler) is called by Webpack, we can access the current Compiler instance object in our custom plugin. We then use the compiler’s hook function to customize the desired operation in the hook function callback (the second argument to the tap method).

For example, the HtmlWebpackPlugin uses the initialize hook function:

apply (compiler) {
  compiler.hooks.initialize.tap('HtmlWebpackPlugin'.() = > {
    // This is a custom operation that is executed when the hook function is called
    // You can check when the hook function is running in the source code with hooks. Initialize. Call
  });
}
Copy the code

These hook functions are called during the life cycle of a WebPack run by searching for hooks in the WebPack source code. Call to see where the hook function is called.

Tap and call are simply defined as listening and triggering.

Tap (name, callback); tap(name, callback); Callback is a function that is called when a hook function is called.

Trigger: hooks. Hook function. Call ([args]).

Similar to on and emit.

Take the afterDone hook function as an example to see when it is called:

Watch.js is only called in watch mode, for example if you run webpack –watch, or use webpack-dev-server, we don’t have watch mode on, so just look at compiler.js:

You can see:

  • afterDoneThe hook function is infinalCallbackCalled in;
  • finalCallbackThe function, as the name suggests, isThe final callback;

In compiler.js, finalCallback() is called several times, for example:

FinalCallback () is executed at the end of the Compiler, whether it succeeds or fails. In other words, afterDone hook function callbacks are eventually executed.

summary

So far, we have

  • Plugin is: a JavaScript object with apply methods;
  • Taking HtmlWebpackPlugin as an example, I know how to install, introduce and use plugin in Webpack project. The plugin has an Apply method whose input is a Compiler instance object.
  • And read the Webpack source code, know
    • When Webpack starts, the Compiler instance object is created;
    • And then callplugin.apply(compiler), pass the current Compiler instance object to plugin;
    • Finally, it passes through the life cycle of the WebPack runThe name of the hook functionTo call the callback function in the hook function.
  • Whether the package succeeds or fails, the hook functionafterDoneWill be called at the end (this is related to the plugin we will write about next).

4. Smart belt

After learning basic concepts and principles, the next step is actual combat.

After webpack is packed, send a notification email to the specified mailbox.

Implementation steps:

  1. Create and CD into the folder we’ll call ityzz-webpack-test-plugin;
  2. npm init -yInitialize the project to generate package.json;
  3. Add the index.js file with the following contents:
// yzz-webpack-test-plugin/index.js
class YzzWebpackTestPlugin {
  apply(compiler) {
    // do something ...
    console.log("Once more! YzzWebpackTestPlugin"); }}module.exports = YzzWebpackTestPlugin;
Copy the code
  1. Introducing this plugin in the previous test-WebPack project:
// test-webpack/webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const YzzWebpackTestPlugin = require(path.resolve(".. /yzz-webpack-test-plugin")); // local relative path import is used here

module.exports = {
  entry: "./src/index.js".mode: "development".output: {
    filename: "main.js".path: path.resolve(__dirname, "dist"),},plugins: [
    new HtmlWebpackPlugin({
      title: "Webpack~",}).new YzzWebpackTestPlugin() // Use our custom plugin]};Copy the code
  1. Run it to see if the plugin is introduced:
cd test-webpack
yarn run webpack or yarn run build
Copy the code

Running results:

Success!

  1. Use the hook function afterDone for subsequent operations after the packaging is complete:
compiler.hooks.afterDone.tap(pluginName, (stats) = > {
  // TODO:Send notification email here
});
Copy the code
  1. The installationnodemailerPlugin for sending mail:

npm i nodemailer -S

For details about how to use nodeMailer, see nodemailer.com/about/

The core code for sending an email:

/ / change the blog: https://www.cnblogs.com/jackson-yqj/p/10154296.html
function emailTo(host, fromEmail, password, toEmail, subject, html, callback) {
  const transporter = nodemailer.createTransport({
    host: host,
    auth: {
      user: fromEmail,
      pass: password // If the sender email address is QQ, it is the authorization code}});var mailOptions = {
    from: fromEmail, / / the sender
    to: toEmail, // The receiver can send multiple messages at the same time, separated by commas
    subject: subject, / / title
  };
  if(html ! =undefined) {
    mailOptions.html = html;// html
  }

  var result = {
    httpCode: 200.message: 'Sent successfully! ',}try {
    transporter.sendMail(mailOptions, function (err, info) {
      if (err) {
        result.httpCode = 500;
        result.message = err;
        callback(result);
        return;
      }
      callback(result);
    });
  } catch (err) {
    result.httpCode = 500; result.message = err; callback(result); }}Copy the code
  1. To call emailTo in afterDone hook function:
compiler.hooks.afterDone.tap(pluginName, (stats) = > {
      const { fromEmail, password, toEmail, host } = this.options;
      if(! fromEmail || ! password || ! toEmail || ! host) {console.log("Incorrect email configuration parameters!");
      } else if (stats) {
        const subject = stats.hasErrors() ? "[ERROR] Webpack failed to pack" : [SUCCESS] Webpack successfully packaged;
        const html = stats.toString() + `<br><div>The ${"Packing time:" + new Date(stats.startTime).toLocaleString() + "-" + new Date(stats.endTime).toLocaleString()}</div>`;
        emailTo(host, fromEmail, password, toEmail, subject, html, function (data) {
          console.log(data); }}}));Copy the code

The options parameter must be entered during plug-in configuration. For details, see Step 8↓.

  1. In the test – webpack/webpack. Config. Incoming js configurationoptionsParameters:
plugins: [
    new YzzWebpackTestPlugin({
      fromEmail: "[email protected]".// Sender email address
      password: "xxx".// If it is a QQ mailbox, it is the QQ mailbox authorization code
      toEmail: "[email protected]".// Recipient email address
      host: "smtp.qq.com" // QQ email server})].Copy the code

Let’s run Webpack and see what it looks like:

Done! ✅

NPM publish can publish a custom plugin to the NPM repository, so that the plugin can be installed by NPM install yzz-webpack-test-plugin -d without having to parse the path:

const YzzWebpackTestPlugin = require("yzz-webpack-test-plugin");
Copy the code

Demo is open source: github.com/youzouzou/y…

5. Other

There are several more important concepts in plugin development, which are not discussed in detail due to lack of time and space:

  1. Compilation[3]
  2. Tapable(tap/tapAsync/tapPromise)[4]

Note: afterDone is a SyncHook and does not support asynchronous mode.

There is a “Writing A Plugin” [5] on the official website, which states:

A plugin for webpack consists of:

  • A named JavaScript function or a JavaScript class.
  • Defines apply method in its prototype.
  • Specifies an event hook to tap into.
  • Manipulates webpack internal instance specific data.
  • Invokes webpack provided callback after functionality is complete.

A Webpack plug-in includes:

  1. A JS function or JS class;
  2. Define the apply method;
  3. Use the tap method of the hook function
  4. Handling webPack instance data in hook function callbacks
  5. In the case of asynchronous tapAsync/tapPromise, the callback callback function is called after the processing is complete.

Disclaimer: this article and project are for beginners only, please use them with caution. If there are mistakes, welcome to point out ~~~

Refer to the article

  • [1] Compiler Example: webpack.js.org/api/node/#c…
  • [2] the compiler hooks: webpack.docschina.org/api/compile…
  • [3] Compilation:webpack.docschina.org/api/compila…
  • [4] Tapable:github.com/webpack/tap…
  • [5] Writing a Plugin:webpack.docschina.org/contribute/…