Webpack is a pleasant and worrying, powerful but requires a certain amount of learning costs. Before exploring the Webpack plug-in mechanism, we need to understand an interesting thing, the Webpack plug-in mechanism is the skeleton of the entire Webpack tool, and Webpack itself is built using this plug-in mechanism. Therefore, in-depth understanding of the Webpack plug-in mechanism, then to carry out the relevant optimization of the project, it will certainly be beneficial.
Webpack Plug-in
Let's go first. Application of Webpack plug-in in project
const=require('myplugin')const=require('webpack')webpack({ ..., plugins: [newMyPlugin()] ...,})
So what are the conditions that can be used as Webpack plugins? In general, the Webpack plugin has the following features:
Independent JS module, exposing the corresponding function
The Apply method on the function prototype injects the compiler object
The corresponding Webpack event hooks are mounted on the compiler object
The callback function of the event hook can get the compiled compilation object, if it is an asynchronous hook can get the corresponding callback
Here's a combination of code to see:
function Myplugin (options) {} //2. The Apply method on a function prototype injects a compiler object myplugin . prototype . apply = function (compiler) { //3.compiler object is mounted on the corresponding Webpack event Hook 4. The callback function of the event hook can get the compiled compilation object
compiler . plugin ( ' emit ' , (Compilation callback) => { ... } ) } //1. Separate JS module, exposing the corresponding function module . exports = Myplugin
This way, the basic outline of the Webpack plug-in is sketched out, at this point there are a few questions,
- Question 1: Why do you define the Apply method on the prototype of a function? After reading the source code found in the source code by
plugin.apply()
calling the plugin.
const= (options,=>{ ... for (constoptions.plugins{ plugin.apply(compiler); } ...}
What is the question 2:compiler object?
Question what is the event hooks on the 3:compiler object?
Question 4: What is the compilation object that can be obtained in the callback function of the event hook?
These questions are also the clues of this article, let us explore one by one.
Compiler object
Compiler is the Webpack Editor object, when the Webpack is called, the compiler object is initialized automatically, the source code is as follows:
//Webpack/lib/webpack.jsConstCompiler= require("./compiler")ConstWebpack=(Options,Callback= {... options= New Webpackoptionsdefaulter().Process(options)//Initialize the Webpack configuration parameters LetCompiler= New Compiler(Options.Context)//Initialize the compiler object, where Options.context is PROCESS.CWD () compiler.Options =Options//Add initialization parameters to compiler New Nodeenvironmentplugin().Apply(compiler)//Add Node environment-related methods to compiler for(ConstPlugin ofOptions.Plugins){ plugin.Apply(compiler); }...}
Finally, the compiler object contains all the Webpack configurable content, and when developing the plugin, we can get all the content related to the Webpack master environment from the compiler object.
Compilation Object
The compilation object represents a single version build and build resource. When you run Webpack, whenever a file change is detected, a new compilation is created to generate a new set of compilation resources. A compiled object shows the current module resources, compiled build resources, changed files, and status information that is tracked.
In combination with the source code to understand the above paragraph, first webpack in each execution will be called compiler.run()
(source location), and then trace the Oncompiled function passed the compilation parameter, you can find compilation from the constructor compilation.
// webpack/lib/Compiler.jsconst=require("./Compilation");newCompilation{ const=newCompilation(this); ... return compilation;}
Have to mention the Tapable library
After introducing the compiler object and the compilation object, we have to mention tapable this library, which exposes all the pub/sub methods associated with the event. and the function Compiler and function compilation inherit from Tapable.
Event Hooks
Event hooks are actually life-cycle functions like the MVVM framework, which can be handled in a specific way at a particular stage. Understanding some of the common event hooks is the pre-condition for writing Webpack plug-ins, here are some common event hooks and their effects:
Hooks |
function |
Parameters |
type |
After-plugins |
After you set up a set of initialization plug-ins |
Compiler |
Sync |
After-resolvers |
After setting up resolvers |
Compiler |
Sync |
Run |
Before reading a record |
Compiler |
Async |
Compile |
Before you create a new compilation |
Compilationparams |
Sync |
Compilation |
Compilation Creation Complete |
Compilation |
Sync |
Emit |
Before generating a resource and outputting it to a directory |
Compilation |
Async |
After-emit |
After generating the resource and outputting it to the directory |
Compilation |
Async |
Done |
Finish compiling |
Stats |
Sync |
Please refer to the official documentation manual, and browse the relevant source code to see the definition of each event hook.
Analysis of plug-in process
Take emit hook for example, the following analysis under the plug-in call source code:
compiler.plugin('emit', (compilation,=>{ // 在生成资源并输出到目录之前完成某些逻辑})
The plugin function called here originates from the Tapable library mentioned above, and its final call stack points to Hook.tapasync (), which acts like Eventemitter on, with the following source code:
//Tapable.jsOptions= {...if(Hook!== undefined){ ConstTapopt= { name: Options.fn.name || "Unnamed Compat plugin", Stage: Options.Stage || 0 }; if(Options.Async)Hook.Tapasync(tapopt, Options.fn); //Inject the callback function of the asynchronous hooks in the plug-in Else Hook.Tap(tapopt, Options.fn); return true; }};
There is an injection must trigger the place, the source code through the Callasync method to trigger the asynchronous event injected before, callasync similar to Eventemitter emit, the relevant source code is as follows:
this.hooks.emit.callAsync(compilation,=>{ ifreturncallback(err); =compilation.getPath(this.outputPath); this.outputFileSystem.mkdirp(outputPath, emitFiles);});
Some in-depth details here will not unfold, say about reading more large-scale projects of the source of two points of experience,
To catch a main line to read, ignoring the details. Otherwise it will waste a lot of time and will be frustrated;
Combined with debugging tools to analyze, many points without debugging tools, it is easy to forgotten how;
Hands-on implementation of a Webpack plugin
Combined with the above knowledge point analysis, it is not difficult to write their own webpack plug-ins, the key lies in the idea. In order to count the effective use of webpack packages in the project, we upgraded the code on the basis of fork Webpack-visualizer and the project address. The effect is as follows:
The plug-in core code is based on the emit hooks mentioned above, as well as the compiler and compilation objects. The code is as follows:
classAnalyzewebpackplugin{ Constructor(OPTs= { filename: ' analyze.html ' }){ This.opts =OPTs} Apply(compiler){ ConstSelf= This compiler.plugin("Emit", function(Compilation,Callback{ LetStats= compilation.GetStats().ToJson({ Chunkmodules: true })//Get the status of each module LetStringifiedstats= JSON.stringify(stats)//service-Side rendering LetHtml= ' <!doctype html><meta charset= "UTF-8" ><title>AnalyzeWebpackPlugin</title><style>${Cssstring}</style><div id= "App" ></div><script>window.stats =${Stringifiedstats};</script><script>${Jsstring}</script> ` compilation.Assets[`${ Self.opts.filename}`]= { //Generate file path Source:()=Html, size:()= HTML.length } Callback()})}}
Resources
See the real Webpack plugin
Webpack official website
Explore the Webpack plugin mechanism