In our previous blog post we analyzed the overall process of webpack from the command line to the completion of packaging, and we know that webpage’s plugin works based on the event mechanism, with the greatest advantage of easy extension. There are many Webpack plugins in the community, but not necessarily suitable for our project. This blog will show you how to start writing a plugin and then analyze the relevant sections of the source code to show you how your plugin works. Know what it is and why. All the test code for this blog series.

Learn to write a plugin from a black box perspective

A black box is one where you ignore how the Webpack plugin works and just look at the webpack website.

Compiler and Compilation are two classes

The Compiler class and Compilation class are included in the source code, and the Compiler class is briefly introduced.

  • CompilerInstantiated at the start of packaging, the instance object contains the environment and parameters associated with packaging, includingoptions,pluginsandloadersAnd so on.
  • CompilationInstantiation occurs every time a file changes and is repackaged, and it inherits fromCompilerIn fact, the object contains andmodulesandchunksRelevant information.

If the black box Angle to write plugin, know these on the line, there is no need to look at the source. The specific contents of the two objects can be printed to see, without further elaboration.

Write a simple plugin

Writing a plugin can be roughly divided into two steps:

  • Define the plugin
  • inwebpack.config.jsReferences this plugin in

Define the plugin

Function HTMLPlugin(options){// Each plugin must define an apply method, Webpack automatically calls this method HTMLPlugin. Prototype. Apply = function (compiler) {/ / apply method will be introduced to the instance of the compiler compiler / / 'emit' is the plug-in to monitor events, Plugins ('emit', function(compilation, callback){// compilation and the next callback function, Callback can not preach / / synchronization event callback compilation. Chunks. ForEach (function (the chunk) {the console. The log (' the chunk. The name ', the chunk. The name). console.log('====================================='); //console.log('chunk.modules', chunk.modules.length); chunk.modules.forEach(function(module){ console.log('module', module.resource); module.fileDependencies.forEach(function(filepath){ //console.log('filepath', filepath); }); }); chunk.files.forEach(function(filename){ let source = compilation.assets[filename].source(); //console.log('file', source); })}); // Call callback callback(); }); } module.exports = HTMLPlugin;Copy the code

See all events that can be listened to. Modules, chunks, and assets on the programmer.

  • Modules, each resource file is compiled into a module, and each module module module.fileDependencies records other modules that the module depends on
  • Compilation. chunks are each configuration item of an entry and modules that call require.ensure. Chunk. modules are modules that chunk contains and modules that the modules depend on. Chunk. files is the last output file of each configuration item, the value of which can be obtained from compilation.assets
  • Compilation. Assets, the final output file of the whole packaging process

It is important to note here that compilation.chunks not only include modules configured in entry in webpack.config.js, but also modules in modules that use require.ensure. When webPack is implemented, it also imitates the CommonJS specification to realize an asynchronous module loading function. When using Require. ensure to load modules, it only downloads modules when it is needed, which can achieve similar lazy loading function and avoid a page that is too large after packaging.

Such as:

// webpack.config.js
entry: {
         index : './index.js',
         detail: './detail.js'
     }Copy the code
// detail.js require('./src/bundle_require.js'); //bundle_require.js has no dependenciesCopy the code
/ / index. Js / / testTapable. Js and temp., js without dependence on the require (". / SRC/tapable testTapable. Js'); let Temp = require.ensure('./src/plugins/temp.js', function(){ console.log('temp is loaded'); }, 'temp'); let temp = new Temp(); console.log('temp is resolved');Copy the code

The result of packing with the above plug-in:

inwebpack.config.jsReferences this plugin in

var HTMLPlugin = require('./src/plugins/HTMLPlugin'); Module.exports = {// plugins:[new HTMLPlugin()],... }Copy the code

The command line execution of Webpack prints the result of the image above.

How does the Plugin work from a white box perspective

Analyze how to call the apply method and how to define a plug-in from the source code.

The pluginapplyHow exactly is the method called

bin/webapck.js

var webpack = require(".. /lib/webpack.js"); compiler = webpack(options);Copy the code

Nothing to explain, look at lib/webpack.js.

lib/webpack.js

const Compiler = require("./Compiler");
compiler = new Compiler();
if(options.plugins && Array.isArray(options.plugins)) {
    compiler.apply.apply(compiler, options.plugins);
}Copy the code

Obviously, compiler has an apply method, which is passed as an argument to the plugins array options.plugins. The Apply method in compiler is actually inherited from Tapable, so you can view its source code by following Tapable to NPM install.

Tapable

Tapable.prototype.apply = function apply() { for(var i = 0; i < arguments.length; i++) { arguments[i].apply(this); }};Copy the code

This method is used to execute the methods in the passed parameters in sequence, with the execution environment this, called the plugin.

pluginHow exactly does the method define a plug-in

The plugin method is derived from Tapable, as is obvious in the Compiler.

Tapable.prototype.plugin = function plugin(name, fn) { if(Array.isArray(name)) { name.forEach(function(name) { this.plugin(name, fn); }, this); return; } if(! this._plugins[name]) this._plugins[name] = [fn]; else this._plugins[name].push(fn); };Copy the code

This is the registered observer in observer mode.

Events that can be used to listen

You can check it in the official documents, and of course you can read the Compiler and Comilation categories carefully to understand. Such as:

Compiler.prototype.compile = function(callback) {
    var self = this;
    var params = self.newCompilationParams();
    self.applyPluginsAsync("before-compile", params, function(err) {
        if(err) return callback(err);

        self.applyPlugins("compile", params);

        var compilation = self.newCompilation(params);

        self.applyPluginsParallel("make", compilation, function(err) {
            if(err) return callback(err);

            compilation.finish();

            compilation.seal(function(err) {
                if(err) return callback(err);

                self.applyPluginsAsync("after-compile", compilation, function(err) {
                    if(err) return callback(err);

                    return callback(null, compilation);
                });
            });
        });
    });
};Copy the code

The compile method is an important way to start packaging. It first fires a before-compile event, which is what you need to do before packaging, which is done by a series of plug-ins, which then fires a compile event, which then fires a make event to start building dependencies.

conclusion

By summarizing the workflow of Webpack in the last blog, we have learned several important classes and basic event processes of Webpack. By checking the introduction of how to write plugin in the official website, we have learned the basic steps of writing plugin from the perspective of black box. The workflow of webPack can be easily summarized from a white-box perspective.

Of course, there are many Compiler and Compilation properties that are not covered. After this blog has taken you into the pit, you can choose a plugin source code to read and write your own plugin.