From 0 to start writing plugins webpack

1 Introduction

Plugin (plugins) is webpackthe first-class hero. It was because there are many widget makes webpackomnipotent. In the webpacksource code also uses a lot of internal plug-in, plug-ins and if used well, can make your work more with less efficiency. Written by someone else with a plug so cool, so why do not we consider a write your own plug it? This article will teach you how to write its own webpackplug-ins.

2. webpack operating mechanism

Before writing plug-ins, we need to first look at webpackthe overall operation mechanism. WebpackA mechanism for the flow of events on the nature of the operation mechanism, in webpackoperation, initialization parameters, start compiling the file, determine entry, compiling module, compiled, generate resources, export resources, completed a series of processes, etc., in this whole process of each node, webpackwill be out a broadcast event, we can listen for each event, at the right time by webpackproviding in APIdoing the right thing.

Easy to understand explanation:

If you do not understand the text above, then our example of a user-friendly plus to explain, as follows:

We webpackcompared a cook, and put webpackthe whole operation process compared to cook fried dish, hot and sour potato dish called silk. Well, from dish to dish is ready to do good dish about to go through a total of the following procedures: vegetables - vegetable - cooking - dish. And we agreed with good cook, chef when each of the next process let him big shout out the name of this procedure, that is to cook a broadcast event, such as when the vegetables cook to start, let the chef shouted: "vegetables." If there are no other requirements, then this channel sour potatoes wire passing through the above four steps can be done after it. However, we have an additional requirement, which is to let the hot and sour potato silk road some of the more spicy. The original chef will follow the normal procedure to do, and will not add extra pepper, then how to do it? At this time, our plugin debut it, we need to write a "plus Pepper" plugin. Before writing this we need to consider at what time plus chili appropriate, that we monitor what procedure, it is clear that, when added in cooking. Then we listen "cooking" This process, once chef broadcast of this event, namely shouting "cooking" when we put pepper added, so that the last to do it more spicy hot and sour potatoes wire will be of some.

In conclusion:

WebpackLike a production line, after going through a series of treatment processes to convert the source file to output.
Each of the duties of this process is a single production line, there are dependencies between multiple processes, and only completed after the current process can be handed over to the next process to deal with.
Like a plug inserted into a functional production line, do the processing resources on the production line at a particular time.

WebpackBy Tapable to organize this complex production line.
WebpackWill broadcast the event during operation, plug it need only listen to events of interest, can be added to this production line, to change the operation of the production line.
WebpackThe event flow mechanism to ensure the orderly plugins, making the whole system scalability well.

OK, now I believe that you have to webpackrun mechanisms have a preliminary understanding. Next, we began to gradually write plug-ins.

3. Writing Plugins

We know that is often used as a way that we typically use someone else's plug-in:

module.exports = {
    entry:'',
    output:{},
    module: {},
    plugins: [
        new XXXPlugin(options)
    ]
}

We can see from the above code, are often used when the plug new XXXPlugin(), a plug-in other words it is a class, the use of the plug-in is newone example of the plug, the plug and the required configuration parameters to the constructor of the class function. Then we can write plug-ins written in the first step, as follows:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

Then, by reading the webpacksource code webpack.js: 46 lines we know:

if (options.plugins && Array.isArray(options.plugins)) {
    for (const plugin of options.plugins) {
        if (typeof plugin === "function") {
            plugin.call(compiler, compiler);
        } else {
            plugin.apply(compiler);
        }
    }
}

When webpackinternal calls when calling the plug-in plug-in instance applymethods, and its incoming compilerobjects, OK, then the second step of writing plugins to come, to add plug-in class applymethods:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

So this complier, then what is? Here we need to elaborate:

In the development Pluginwhen the two objects is the most common Compilerand Compilationthey are inherited from Tapable, is Pluginand Webpacka bridge between.
CompilerAnd Compilationmeanings are as follows:

  • CompilerObject represents a complete webpackenvironment configuration. This object start webpackbeing disposable establish and configure all operational settings, including options, loaderand plugin. When webpackapplying a plug-in environment, the plug-in will receive this Compilerreference object. You can use it to access webpackthe main environment.
  • CompilationObject represents a version construction resources. When running webpackthe development environment middleware, whenever a file change is detected, it will create a new Compilationorder to generate a new set of compiled resources. A Compilationtarget performance of the current resource module, compiled resources, change the file, and tracked dependent status information. CompilationObject also provides a number of key timing correction, choose to use when doing a custom plug-ins for processing.

CompilerAnd Compilationthe difference is: Compilerthe representative of the whole Webpackfrom start to close the life cycle, but Compilationonly represent a new compilation.

Use Compilerobjects you can access webpackthe main environment, you can listen to webpacka series of events during the run out of the broadcast. OK, here we thoroughly understand the core part of the original is a plug-in applymethod, the method in this by listening webpackand doing the right thing at the right time event broadcasts out of. At this point, you may also have a question: do we monitor what event? Or webpack are broadcast throughout the process which events? Next, we have to read through the source code webpackto see the entire run out which events are broadcast.

4. webpack workflow

Webpack The workflow is a serial process, the following procedures will be followed from start to finish:

  • Initialization parameters: from the configuration file and Shellread in consolidated statement parameters, obtain the final parameters;
  • Start the compilation: spend parameter initialization step to get the Compilerobjects, load all plug-in configuration, implementation object runmethod to begin compiling;
  • OK Entrance: According to configuration entryto identify all entry documents
  • Compile the module: From the entrance file, calling on all configured Loader module to compile, then find the module dependent module, and then recursively this step until all files have been dependent on the entrance of the process in this step;
  • Completion module compilation: After Step 4 using LoaderAfter all translation modules, each module obtained after the final content is translated and the dependencies between them;
  • Output Resources: The dependencies between the inlet and the modules are assembled into one comprising a plurality of modules Chunk, and then each of the Chunklast opportunity to switch into a single file is added to the output list, this step can be modified to output content;
  • Output completion: After determining the content of the output well, according to the configuration to determine the output of the path and file name, the file contents are written to the file system.

In the above process, Webpackwill be broadcast at a particular point in time a specific event, after listening to plug in the event of interest will perform a specific logic, and plug-ins can call the Webpackprovided APIchange Webpackoperation results.

According to the broadcast of the event can be divided roughly into three phases: initialization phase, the compilation stage, the output stage. Some common events that occurred in three stages as follows.

4.1 initialization phase

Event name Explanation
Initialization parameters Read parameters from configuration files and merge Shell statement, obtain the final parameters.
Instantiate Compiler 1. spend Compiler initialization parameters obtained in step examples 2.Compiler responsible for the file and start listening 3.Compiler instance contains a complete compilation Webpack configuration, only a global Compiler instance.
Load plugins 1. Plug in turn calls the apply method, so that the plug can monitor all the events subsequent nodes. At the same time to pass compiler plugin instances cited, in order to facilitate the plug-in API Webpack calls provided by the compiler.
environment Began to use Node.js-style file system to the compiler object to facilitate the subsequent search for and read documents
entry-option Read Entrys configured for each instance of a corresponding Entry EntryPlugin, recursive resolution to prepare for the later work of Entry
after-plugins apply method invocation finished all the built-in configuration and plug-ins
after-resolvers Depending on the configuration initialization resolver, resolver is responsible for finding the path specified in the file system file

4.2 compile phase

Event name Explanation
run Start a new compilation
watch-run And run similar, except that it is to start compiling in listening mode, you can get in this event which files have changed leading to restart a new compilation
compile The event is to tell the compiler to a new plug-in to start, but will pass compiler plug-in objects.
compilation When Webpack when running in development mode, every time detects a file change, a new Compilation will be created. A Compilation object contains the current module resources, compiled resources, changes in files. Compilation object also provides many events callbacks for plug-ins do expand.
make Compilation has been created a new master began to compile
after-compile Once the implementation is completed Compilation
invalid When faced with the file does not exist, the file compiler errors will trigger this abnormal event, the event does not cause Webpack exit

4.3 output stage

Event name Explanation
seal All the modules and their dependencies Loader module through the conversion is complete, start generating of a dependency Chunk
Addcudak Generating resources
createChunkAssets Create Resource
getRenderManifest A description of the file to be rendered
render Rendering Source
afterCompile Compile End
shouldEmit All files required output has been generated good, ask which files need to plug the output and which do not.
emit After determining which files to good output, perform file output, where you can access and modify the output.
emitRecords Written record
done The successful completion of a full compilation and output processes
failed 如果在编译和输出的流程中遇到异常,导致Webpack 退出, 就会直接跳转到本步骤,插件可以在本事件中获取具体的错误原因

更多的事件钩子请求查看这里:Compiler对象事件Compilation对象事件

5. 继续编写插件

知道了广播了哪些事件之后,我们就可以在apply中监听事件并编写相应的逻辑啦。如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.'compiler事件名称'.tap('XXXPlugin', (compilation)=> {
            //编写相应逻辑
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

当我们监听Compiler对象事件中的compilation事件时,此时可以在回调函数中再次监听compilation对象里的事件,如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.compilation.tap('XXXPlugin', (compilation)=> {
            compilation.hooks.'compilation事件名称'.tap('optimize', () => {
                //编写相应逻辑
                
            });
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

5.1 异步插件

有一些事件使用了异步钩子AsyncHook,此时在监听这些事件的时候我们就需要使用tapAsynctapPromise,并且还需要额外传入一个 callback 回调函数,在插件运行结束时,必须调用这个回调函数,如下:

class XXXPlugin{
    // 在构造函数中获取用户给该插件传入的配置
    constructor(options) {
        this.options=options;
    }
    // Webpack 会调用 XXXPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply(compiler) {
        // 监听某个事件
        compiler.hooks.emit.tapAsync('XXXPlugin', (compilation,callback)=> {
            setTimeout(function () {
                console.log('异步任务完成');
                callback();
            },500);
        });
    }
}
// 导出 Plugin
module.exports=XXXPlugin;

OK,以上就是编写一个webpack插件所有需要用到的东西,接下里我们就来编写一个简单的插件做个示例。

6. 插件示例

在这里我们编写一个简单的插件,该插件的功能是:将webpack输出的所有文件打包压缩成一个zip压缩包,该插件接收一个配置参数filename,表示最终生成的压缩包文件名称。其代码如下:

定义ZipPlugin插件:

const { RawSource } = require("webpack-sources");
const JSZip = require("jszip");
class ZipPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.emit.tapAsync("ZipPlugin", (compilation, callback) => {
      var zip = new JSZip();
      for (let filename in compilation.assets) {
        const source = compilation.assets[filename].source();
        zip.file(filename, source);
      }
      zip.generateAsync({ type: "nodebuffer" }).then(content => {
        compilation.assets[this.options.filename || 'fileList.zip'] = new RawSource(content);
        callback();
      });
    });
  }
}
module.exports = ZipPlugin;

代码说明:

  1. 首先获取到用户传入的配置对象,将其存入this.options内。
  2. 我们要想在webpack输出完成后将输出的所有文件打包成zip压缩包,那么就必须监听webpack输出完成后的那个事件,即emit事件。并且该事件为异步事件,所以使用tabAsync监听。
  3. 通过webpack的输出资源对象compilation.assets获取到输出的所有文件,遍历这些文件,使用JSZip包将这些文件都放入一个zip压缩包内。
  4. 然后将这个压缩包挂载到webpack的输出资源对象compilation.assets上,使其跟随输出资源一同被输出,该压缩包的文件名使用用户传入的filename,如果用户未传入则使用默认的名字fileList.zip
  5. 最后由于是异步事件,在运行结束时,必须调用callback()

使用ZipPlugin插件:

const ZipPlugin  = require('./src/plugins/ZipPlugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve('dist'),
        filename: 'bundle.js'
    },
    plugins: [
      // 使用ZipPlugin插件
      new ZipPlugin({
          filename:'NLRX.zip'
      })
    ]
}

7. 总结

那么我们可以简单总结一下,一个插件的构成部分:

  • 一个插件就是一个类。
  • 在插件类上定义一个 apply 方法。
  • apply方法中监听一个webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调。

相信读完这篇文章,你应该就能写出一个自己的插件了。

(完)

Guess you like

Origin www.cnblogs.com/wangjiachen666/p/11586438.html