Webpack principle
See all documentation pages: Front-end Development Documentation for more information. Text link: Webpack principle, text advertising mode box occlusion, reading experience is not good, so the cost of the text, easy to find.
Working principle summary
The basic concept
To understand the principles of Webpack, there are a few core concepts that need to be understood:
Entry
: Entry, the first step in Webpack’s execution of the build will start with Entry, which can be abstracted into input.Module
: module, everything in Webpack is a module, a module corresponds to a file. Webpack will recursively find all dependent modules from the configured Entry.Chunk
: A Chunk of code. A Chunk is a combination of multiple modules and is used for code merging and splitting.Loader
: Module converter, used to convert the original content of the module into new content as required.Plugin
An extension that broadcasts events at specific times in the Webpack build process. The plug-in can listen to these events and do something at specific times.
Generalization process
The running process of Webpack is a serial process. From start to finish, the following processes are executed in sequence:
- Initialization parameters: read and merge parameters from configuration files and Shell statements to get the final parameters;
- Start compiling: Initialize the Compiler object with the parameters obtained in the previous step, load all configured plug-ins, and execute the run method of the object to start compiling.
- Determine entry: Find all entry files according to the entry in the configuration;
- Module compilation: Start from the entry file, call all configured loaders to translate the module, then find out the module that the module depends on, and then recurse this step until all the entry dependent files have been processed by this step;
- Completion of module compilation: After all modules are translated by Loader in step 4, the final content of each module after translation and the dependency between them are obtained;
- Output resources: According to the dependencies between the entry and the module, assemble them into chunks containing multiple modules, and then convert each Chunk into a separate file and add it to the output list. This step is the last chance to modify the output content.
- Output completion: After the output content is determined, the output path and file name are determined according to the configuration, and the file content is written to the file system.
In the above process, Webpack will broadcast specific events at specific points in time, the plug-in will execute specific logic after listening to the event of interest, and the plug-in can call the API provided by Webpack to change the Webpack running result.
Process details
The Webpack build process can be divided into three phases:
- Initialization: Start the build, read and merge configuration parameters, load Plugin, and instantiate Compiler.
- Compile: From the Entry, the corresponding Loader is called for each Module to translate the contents of the file, and then find the Module that the Module depends on, and compile the process recursively.
- Output: Combine the compiled modules into chunks, convert the chunks into files, and output to the file system.
If the build is executed only once, each of the above phases will be executed once in sequence. However, when the listening mode is enabled, the process becomes as follows:
A number of events occur in each major phase, which Webpack broadcasts for use by the Plugin, as described below.
Initialization phase
The event name | explain |
---|---|
Initialization parameter | Read and merge parameters from configuration files and Shell statements to get the final parameters. This process also executes the plug-in instantiation statement in the configuration filenew Plugin() . |
instantiationCompiler |
Initialize the parameters obtained in the previous stepCompiler Instance,Compiler Responsible for file listening and start compilation.Compiler The example contains the completeWebpack Configuration, there is only one global configurationCompiler Instance. |
Load the plug-in | Call the plug-in in turnapply Method to enable the plug-in to listen to all subsequent event nodes. And pass it to the plug-incompiler Instance for easy passage by plug-inscompiler Call the API provided by Webpack. |
environment |
Start applying the Node.js style file system to the Compiler object to facilitate subsequent file finding and reading. |
entry-option |
Read configuredEntrys For eachEntry Instantiate a correspondingEntryPlugin , is the followingEntry To prepare for recursive resolution work. |
after-plugins |
After calling all the built-in and configured plug-insapply Methods. |
after-resolvers |
The initialization is complete according to the configurationresolver .resolver Is responsible for finding files at the specified path in the file system. |
The blank space | The blank space |
The blank space | The blank space |
The blank space | The blank space |
Compilation phase
The event name | explain |
---|---|
run |
Start a new build. |
watch-run |
andrun Similarly, the difference is that it starts compilation in listening mode, and in this event it is possible to obtain which files have changed to cause a new compilation to be restarted. |
compile |
This event is meant to tell the plug-in that a new build is about to start, and is also given to the plug-incompiler Object. |
compilation |
whenWebpack When running in development mode, every time a file change is detected, a newCompilation Will be created. aCompilation The object contains the current module resources, compiled generated resources, changed files, and so on.Compilation Objects also provide a number of event callbacks for plug-ins to extend. |
make |
A newCompilation We’re done. We’re about to startEntry Start reading files according to file type and configurationLoader Compile the file, then find out the files that the file depends on, recursively compile and parse. |
after-compile |
At a timeCompilation The execution is complete. |
invalid |
This event is triggered when an exception such as a nonexistent file or a file compilation error is encountered and does not cause Webpack to exit. |
The blank space | The blank space |
The blank space | The blank space |
In the compilation phase, the most important events are the Compilation events, because the Loader is called to complete the conversion of each module in the Compilation phase. In the Compilation phase, there are many small events. They are:
The event name | explain |
---|---|
build-module |
Use the corresponding Loader to convert a module. |
normal-module-loader |
After a module has been converted by Loader, useacorn Parse the transformed content and output the corresponding abstract syntax tree (AST ) to facilitate analysis of the code behind Webpack. |
program |
Start from the entry module of the configuration, analyze its AST, and add it to the list of dependent modules when importing other module statements such as require. Meanwhile, recursively analyze the newly found dependent modules, and finally find out the dependency relationship of all modules. |
seal |
After all modules and their dependent modules are converted by Loader, Chunk generation begins based on the dependency. |
The output stage
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 ones do not. |
emit |
Once you have determined which files to output, you perform file output, where you can retrieve and modify the output. |
after-emit |
The file output is complete. |
done |
Complete a complete compilation and output process successfully. |
failed |
If an exception is encountered during the compilation and output process that causes Webpack to exit, this step will be skipped directly, and the plug-in can obtain the specific cause of the error in this event. |
In the output phase, the transformed results and dependencies of each module are obtained, and the related modules are combined into chunks. In the output phase, the template is used to generate the final output of the file based on the Chunk type.
Output file analysis
Although you learned how to use Webpack in the previous section and have a general idea of how it works, have you ever thought about what bundle.js looks like as output from Webpack? Why were the original module files merged into a single file? Why does bundle.js run directly in the browser? This section will clarify the above issues.
This complex looking code is actually an instant-execute function, which can be abbreviated as:
(function(modules) {// emulate the require statementfunction __webpack_require__() {} // Execute the 0th module __webpack_require__(0) in the array of all modules; })([/* hold an array of all modules */])Copy the code
The reason bundle.js can run directly in the browser is that the output file defines a load function that can be executed in the browser via the __webpack_require__ function to simulate the require statement in Node.js.
The reason the separate module files were merged into a single bundle.js is that the browser cannot load the module files locally as quickly as Node.js, and must load the files that are not yet available via network requests. If there are a lot of modules, the load time will be long, so store all modules in the array and perform a network load.
If you take a closer look at the __webpack_require__ implementation, you’ll also see that Webpack is optimized for caching: loaded modules are not executed a second time, the results are cached in memory, and when a module is accessed a second time, the cached return value is read directly from memory.
The output when the code is split
For example, change main.js in the source code to look like this:
// Load show.js import()'./show').then((show) => {//'Webpack');
});
Copy the code
The rebuild outputs two files, the execution entry file bundle.js and the asynchronous load file 0.bundle.js.
The contents of 0.bundle.js are as follows:
// load the module webpackJsonp contained in this file (0.bundle.js) (// ID of the module [0] stored in other files), // load the module webpackJsonp contained in this file (0.bundle.js) (// ID of the module [0] stored in other files), //function (module, exports) {
function show(content) {
window.document.getElementById('app').innerText = 'Hello,'+ content; } module.exports = show; })]);Copy the code
Bundle.js contains the following contents:
Bundle.js is very similar to bundle.js, but the difference is:
- One more
__webpack_require__.e
Used to load files corresponding to chunks that are partitioned and need to be loaded asynchronously. - One more
webpackJsonp
The module () function is used to install a module from a file that is loaded asynchronously.
When using CommonsChunkPlugin to extract common code, the output file is the same as when using asynchronous loading, with __webpack_require__.e and webpackJsonp. The reason is that extracting common code and asynchronous loading are essentially code splits.
Write the Loader
Loader is like an interpreter, which can translate the source file and output the new result, and a file can be chained through multiple translators.
Take processing SCSS files as an example:
- The SCSS source code will be handed in first
sass-loader
Convert SCSS to CSS; - the
sass-loader
The output CSS is handed overcss-loader
Process, find the dependent resources in the CSS, compress the CSS, etc. - the
css-loader
The output CSS is handed overstyle-loader
Processing, converted to JavaScript code loaded via a script;
It can be seen that the above process needs to be sequentially executed, first sass-loader, then CSS-loader, then style-loader. The Webpack configuration is as follows:
The duties of a Loader
As you can see from the above example, a Loader has a single responsibility and only needs to perform one conversion. If a source file needs to be converted through multiple steps before it can be used, it is converted through multiple loaders. When multiple loaders are called to convert a file, each Loader will be executed in a chain sequence. The first Loader will get the original content to be processed, and the results of the previous Loader will be passed to the next Loader for further processing. The final Loader returns the processed final result to Webpack.
So, when you develop a Loader, keep its responsibilities simple, you only care about input and output.
Loader basis
Since Webpack runs on node.js, a Loader is essentially a Node.js module that needs to export a function. The job of this exported function is to get the original content before processing, perform processing on the original content, and return the processed content.
One of the simplest Loader source code is as follows:
module.exports = function(source{/ /sourceThis function needs to return the processed content. For simplicity, this function returns the original content directly, which means that the Loader does not do any conversionreturn source;
};
Copy the code
Since Loader runs in Node.js, you can call any of the node.js apis, or install a third-party module to do this:
const sass = require('node-sass');
module.exports = function(source) {
return sass(source);
};
Copy the code
Loader advanced
These are just the simplest loaders. Webpack also provides some apis for Loaders to call, which are described below.
Get the Loaderoptions
In the Webpack configuration that handles SCSS files at the top, options is passed to csS-Loader to control the CSS -loader. How to get options from the user in your own Loader? You need to do this:
const loaderUtils = require('loader-utils');
module.exports = function(sourceConst Options = loaderUtils.getoptions (this); const Options = loaderUtils.getoptions (this);return source;
};
Copy the code
Return other results
All of the above loaders only return the converted content of the original content, but in some cases you need to return something other than the content.
For example, if you use Babel-Loader to convert ES6 code, it also needs to output the Source Map of the converted ES5 code to facilitate debugging of the Source code. To return the Source Map to Webpack along with the ES5 code, write:
module.exports = function(source) {// tell Webpack the returned result via this.callback this.callback(null,source.sourceMaps); // When you use this.callback to return content, the Loader must return undefined, // to let Webpack know that the result returned by the Loader is in this.callback, and notreturn 中
return;
};
Copy the code
This. Callback is an API that Webpack injects into Loader to facilitate communication between Loader and Webpack. The following is how to use this.callback:
This callback (/ / when unable to convert the original content, give Webpack returns an Error err: Error | null, / / the original content converted content the content: String | Buffer, / / for the converted concluded the content of the original content of the Source Map, convenient debuggingsourceMap? : SourceMap, // If the AST syntax tree is generated from the original content, we can return the AST, // to facilitate the reuse of the AST by the Loader that needs the AST, so as to avoid repeated AST generation and improve the performance of abstractSyntaxTree? : AST );Copy the code
Source Maps are time-consuming to generate and are usually generated in development environments and not in other environments to speed up build. For this purpose Webpack provides the this.sourceMap API for Loader to tell the Loader whether the user needs the Source Map in the current build environment. If you are writing a Loader that generates a Source Map, take this into account.
Synchronous and Asynchronous
Loaders can be classified into synchronous and asynchronous loaders. All loaders described above are synchronous loaders, because their conversion process is synchronous and the conversion result is returned after completion. In some cases, however, the transition steps can only be done asynchronously. For example, if you need a network request to get the result, the network request will block the entire build, making the build very slow.
When the transformation step is asynchronous, you can do this:
module.exports = function(sourceVar callback = this.async(); var callback = this.async(); someAsyncOperation(source.function(err, result, sourceCallback (err, result, err) {sourceMaps, ast);
});
};
Copy the code
Processing binary data
By default, Webpack passes the original content to Loader as a STRING encoded in UTF-8 format. In some cases, however, Loader processes binary files instead of text files. For example, file-Loader needs Webpack to pass in binary data to Loader. To do this, you need to write a Loader like this:
module.exports = function(source) {
// 在 exports.raw === trueWhen Webpack is passed to LoadersourceIs of type Buffersource instanceof Buffer === true; // Loader can also return type Buffer // in exports.raw! = =trueLoader can also return the result of type Bufferreturn source; }; // Tell Webpack whether the Loader needs binary data module.exports.raw = via the exports.raw propertytrue;
Copy the code
The most important code is the last line of module.exports.raw = true; Without the line Loader can only get the string.
Cache acceleration
In some cases, some transformations are computation-intensive and time consuming, and if repeated transformations are performed again with each build, the build will become very slow. To this end, Webpack will cache the results of all Loaders by default, which means that the corresponding Loader will not be called again to perform the conversion operation if the files that need to be processed or the files that depend on them have not changed.
If you want Webpack not to cache the results of this Loader, you can do this:
module.exports = function(source) {// Disable this.cacheable();false);
return source;
};
Copy the code
Other Loader API
In addition to the Webpack apis mentioned above that can be called from the Loader, there are the following common apis:
this.context
: indicates the directory where the file currently being processed is stored/src/main.js
,this.context
Is equal to/src
.this.resource
: The full request path for the file currently being processed, includingquerystring
, e.g./src/main.js? name=1
.this.resourcePath
: Indicates the path of the file currently being processed, for example/src/main.js
.this.resourceQuery
: Currently processing filesquerystring
.this.target
: equals Target in the Webpack configuration.this.loadModule
: When Loader processes a file and relies on the processing results of other files to obtain the result of the current file, it can be usedthis.loadModule(request: string, callback: function(err, source, sourceMap, module))
To getrequest
Processing result of the corresponding file.this.resolve
: likerequire
Statement to get the full path to the specified fileresolve(context: string, request: string, callback: function(err, result: string))
.this.addDependency
: Adds a dependent file to the current processing file, so that if the dependent file changes, Loader will be called to process the file again. Use method:addDependency(file: string)
.this.addContextDependency
And:addDependency
Similar, butaddContextDependency
Is to add the entire directory to the dependency of the file currently being processed. Use method:addContextDependency(directory: string)
.this.clearDependencies
: clears all dependencies of the file being processedclearDependencies()
.this.emitFile
: Outputs a fileemitFile(name: string, content: Buffer|string, sourceMap: {... })
.
Loading the Local Loader
During the development process of Loader, to test whether the written Loader works properly, it needs to be configured in Webpack before it can be called. In the previous sections, loaders are installed via Npm. To use loaders, use the Loader name directly.
module.exports = {
module: {
rules: [
{
test: /\.css/,
use: ['style-loader'[,},]},};Copy the code
Using a locally developed Loader in the same way can be cumbersome, as you need to make sure that the source code for the Loader is in node_modules. To do this you need to publish the Loader to the Npm repository before installing it to your local project.
There are two convenient ways to solve the above problems, respectively as follows:
Npm link
Npm Link is designed to develop and debug native Npm modules. It links the source code of a local module under development to the node_modules directory of the project, allowing the project to use the native Npm module directly, without releasing the module. Since it is implemented by means of soft link, the local Npm module code is edited, and the edited code can be used in the project.
The steps to complete the Npm link are as follows:
- Ensure that the local Npm module being developed (that is, the Loader being developed) is
package.json
It has been configured correctly; - Run the command in the root directory of the local Npm module
npm link
To register local modules globally; - Execute in the project root directory
npm link loader-name
Link the local Npm module registered globally in step 2 to the project’snode_moduels
Below, one of themloader-name
In step 1package.json
The module name configured in the file.
After linking the Loader to the project, you can use the local Loader just like a real Npm module.
ResolveLoader
ResolveLoader is used to configure how Webpack finds loaders. By default, only node_modules is looked for. In order for Webpack to load a Loader in a local project, you need to modify resolveloader.modules.
If the local Loader is in./loaders/loader-name in the project directory, the configuration is as follows:
Module. Exports = {resolveLoader:{module. Exports = {resolveLoader:{module.'node_modules'.'./loaders/'].}}Copy the code
After this configuration, Webpack will first look for a Loader under node_modules, and if it can’t find a Loader, it will look under./loaders/.
In actual combat
With a lot of theory out of the way, let’s get practical and write a Loader that solves a real problem.
This Loader is called comment-require-loader and is used to transfer comment syntax from JavaScript code:
// @require '.. /style/index.css'
Copy the code
Converted to:
require('.. /style/index.css');
Copy the code
The use case for this Loader is to properly load JavaScript written for Fis3 that has a dependent CSS file loaded via annotations.
The Loader can be used as follows:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['comment-require-loader'], // use comment-require-loader to convert include: [path.resolve(__dirname,'node_modules/imui')]}]}};Copy the code
The implementation of this Loader is very simple. The complete code is as follows:
function replace(source) {// Use the re to put // @require'.. /style/index.css'Converted to require ('.. /style/index.css');
return source.replace(/(\/\/ *@require) +((+ (' '| ").|")).*/, 'require($2); '); } module.exports = function (content) { return replace(content); };Copy the code
Writing a Plugin
Webpack uses the Plugin mechanism to make it more flexible to suit various application scenarios. During the life of Webpack, a number of events are broadcast, and the Plugin can listen for these events and change the output when appropriate using the apis provided by Webpack.
The code for a basic Plugin looks like this:
Class BasicPlugin{// constructor(options){// Webpack will call the apply method of BasicPlugin instance to the plugin instance Compiler object apply(compiler){compiler.plugin('compilation'.function(compilation) {}} // Export Plugin module.exports = BasicPlugin;Copy the code
To use this Plugin, the configuration code is as follows:
const BasicPlugin = require('./BasicPlugin.js');
module.export = {
plugins:[
new BasicPlugin(options),
]
}
Copy the code
After Webpack starts, the new BasicPlugin(options) is executed to initialize a BasicPlugin acquisition example while reading the configuration. After the compiler object is initialized, the plugin instance is passed the Compiler object by calling basicplugin.apply (compiler). After obtaining the compiler object, the plug-in instance can listen to events broadcast by Webpack through the compiler.plugin(event name, callback function). And you can use the Compiler object to manipulate Webpack.
With the simplest Plugin above, I believe you have a general idea of how plugins work, but there are many details to pay attention to in the actual development, which will be described in detail below.
Compiler
和 Compilation
The two most commonly used objects when developing a Plugin are Compiler and Compilation, which are the bridge between the Plugin and Webpack. Compiler and Compilation have the following meanings:
- The Compiler object contains all configuration information for the Webpack environment, including
options
.loaders
.plugins
This information, this object is instantiated at Webpack startup, is globally unique and can be simply understood as a Webpack instance; - The Compilation object contains the current module resources, compiled and generated resources, and changed files. When Webpack is running in development mode, a new Compilation is created each time a file change is detected. The Compilation object also provides a number of event callbacks for plug-in extensions. Compiler objects can also be read through Compilation.
The difference between Compiler and Compilation is that Compiler represents the entire Webpack life cycle from startup to shutdown, whereas Compilation represents just one new Compilation.
Flow of events
Webpack is like a production line, going through a series of processes to turn source files into output. Each process in the production line has a single responsibility, and there are dependencies between multiple processes that can only be handed over to the next process after the current process has been completed. A plug-in is like a feature that is inserted into a production line to process resources on the production line at a specific time.
Webpack uses Tapable to organize this complex production line. Webpack broadcasts events as they run, and plugins can be added to the production line simply by listening for the events they care about. The event flow mechanism of Webpack guarantees the order of plug-ins and makes the whole system extensibility very good.
Webpack’s event-flow mechanism applies the Observer pattern, much like EventEmitter in Node.js. Compiler and Compilation both inherit from Tapable, and you can broadcast and listen to events directly on the Compiler and Compilation objects as follows:
/** * broadcast event * event-name = event name * params = attached parameter */ compiler. Apply ()'event-name',params); /** * Listen for an event named event-name. When event-name occurs, the function is executed. * Also, the params argument in the function is the argument that comes with the broadcast event. */ compiler.plugin('event-name'.function(params) {
});
Copy the code
Similarly, the use of compilation. Apply and compilation. Plugin is the same as above.
When developing a plug-in, you might not know where to start because you don’t know which events to listen for to complete the task.
There are two other points to keep in mind when developing plug-ins:
- As long as the Compiler or Compilation object can be obtained, new events can be broadcast, so newly developed plug-ins can also broadcast events for other plug-ins to listen and use.
- The Compiler and Compilation objects passed to each plug-in are the same reference. This means that changing the Compiler or Compilation object properties in one plug-in will affect later plug-ins.
- Some events are asynchronous, and these asynchronous events take two arguments. The second argument is a callback function that needs to be called to notify Webpack when the plug-in finishes processing the task before moving on to the next process. Such as:
compiler.plugin('emit'.function(compilation, callback) {// Support processing logic // Execute the callback after processing to notify Webpack // If you do not execute the callback, the running process will always be stuck in the callback(); });Copy the code
Commonly used API
Plug-ins can be used to modify output files, increase output files, even improve Webpack performance, and so on. Plug-ins can do a lot of things by calling the apis provided by Webpack. Since Webpack provides a large number of apis, many of which are rarely used, and space is limited, here are some commonly used apis.
Read output resources, code blocks, modules, and their dependencies
Some plug-ins may need to read the results of Webpack processing, such as output resources, code blocks, modules and their dependencies, in order to proceed to the next step.
When the Emit event occurs, it represents that the transformation and assembly of the source file has been completed, and the resulting output resource, code block, module and its dependencies can be read, and the contents of the output resource can be modified. The plug-in code is as follows:
See the Pen Emit click preview by whjin (@whjin click preview) on CodePen.
By default, Webpack only monitors entries and dependent modules for changes. In some cases, a project may need to introduce new files, such as an HTML file. Since JavaScript files do not import HTML files, Webpack does not listen for changes to HTML files, and editing HTML files does not trigger a new Compilation. To listen for changes to the HTML file, we need to add the HTML file to the list of dependencies. To do this, use the following code:
compiler.plugin('after-compile', (compilation, callback) => {// add HTML files to the file dependencies list so Webpack can listen to HTML module files, In the HTML template file changes to restart a compilation. FileDependencies. Push (filePath); callback(); });Copy the code
Modifying output Resources
In some scenarios, plug-ins need to modify, add, or delete output resources. To do this, they need to listen for the Emit event. When the Emit event occurs, the files corresponding to all modules’ transforms and code blocks have already been generated. So the Emit event is the last time to modify the Webpack output resource.
All resources to be output are stored in compilation.assets, which is a key-value pair. The key is the name of the file to be output, and the value is the content of the file.
The code to set up compilation. Assets is as follows:
compiler.plugin('emit', (compilation, callback) => {// Set the output resource named fileName compilation.assets[fileName] = {// Return the contents of the filesource: () => {// fileContent can be either a string representing a text file or a Buffer representing a binary filereturnfileContent; }, // return file size size: () => {return Buffer.byteLength(fileContent, 'utf8'); }}; callback(); });Copy the code
Read the code of Compilation. Assets as follows:
compiler.plugin('emit', (compilation, callback) => {const asset = compilation. Assets [fileName]; // Get the content of the output resource asset.source(); Asset.size (); callback(); });Copy the code
Determine which plug-ins Webpack uses
When developing a plug-in, you may need to make the next decision based on whether another plug-in is used in the current configuration, so you need to read Webpack’s current plug-in configuration. To determine if the ExtractTextPlugin is currently in use, use the following code:
// Indicates that the current configuration uses the ExtractTextPlugin. // The compiler parameter is the one that Webpack passes in apply(compiler)functionHasExtractTextPlugin (compiler) {// List of plugins used in the current configuration const plugins = compiler.options.plugins; // Go to the plugins to find an instance of the ExtractTextPluginreturnplugins.find(plugin=>plugin.__proto__.constructor === ExtractTextPlugin) ! = null; }Copy the code
In actual combat
Let’s take a practical example and walk you through the steps of implementing a plugin.
The plugin is called EndWebpackPlugin and is used to perform additional operations when Webpack is about to exit, such as publishing the output file to the server after Webpack has successfully compiled and exported the file. The plug-in also distinguishes between successful Webpack builds. The method to use this plug-in is as follows:
Module.exports = {plugins:[// EndWebpackPlugin was passed with two arguments, New EndWebpackPlugin(() => {// Webpack was built successfully and the file output will be executed here, }, (err) => {// Webpack build failed, err is the cause of the error console.error(err);})]}Copy the code
To implement the plug-in, you need to use two events:
done
: Occurs when Webpack is about to exit after a successful build and the file has been exported;failed
: occurs when a build exception causes the build to fail and Webpack is about to exit;
Implementing the plug-in is very simple. The complete code is as follows:
class EndWebpackPlugin {
constructor(doneCallback, failCallback) {// save the Callback passed in the constructor this.donecallback =doneCallback;
this.failCallback = failCallback;
}
apply(compiler) {
compiler.plugin('done', (stats) => {// indoneEventdoneCallback
this.doneCallback(stats);
});
compiler.plugin('failed', (err) => {// Callback failCallback this. FailCallback (err); }); } // export module. Exports = EndWebpackPlugin;Copy the code
As you can see from the development of this plug-in, finding the right event point to complete the function is particularly important when developing a plug-in. In the overview of how it works, Webpack broadcasts common events during runtime so you can find the events you need.
Debugging Webpack
When writing Webpack plugins and loaders, the results may not be what you expect, just like when you encounter a strange Bug in your code. For bugs that can’t be seen at a glance, you usually need to debug the source code to find the problem.
Debugging can be done via console.log, but it’s not very convenient or elegant. This section will show you how to debug the plug-in code in the overview of how breakpoints work. Since Webpack runs on top of Node.js, debugging Webpack is relative to debugging a Node.js program.
Debug in Webstorm
Webstorm is integrated with node.js debugging tools, so using Webstorm to debug Webpack is very simple.
1. Set breakpoints
Set a breakpoint where you think there might be a problem. Click on the edit area code and the red dot to the left indicates that the breakpoint is set.
2. Configure the entry
Tell Webstorm how to start Webpack. Since Webpack is actually a Node.js application, you need to create a new node.js execution entry.
There are three points to note in the above configuration:
Name
Set up adebug webpack
, like setting an alias, easy to remember and distinguish;Working directory
Set to the root directory of the project where the plug-in needs to be debugged.JavaScript file
The node.js execution entry file is set to the Webpack execution entry filenode_modules/webpack/bin/webpack.js
.
3. Start debugging
After the above two steps, the preparation is complete. Let’s start debugging by selecting debug WebPack.
4. The execution reaches the breakpoint
When started, the program will stop at the breakpoint, where you can easily view the current state of the variable and find the problem.
The principle of summary
Webpack is a huge Node.js application, and if you read the source code, you’ll see that it takes a lot of code to implement a complete Webpack. But you don’t need to know all the details, just the overall architecture and some of the details.
For Webpack users, it is a simple and powerful tool; For Webpack developers, it is a highly scalable system.
Webpack succeeds because it hides the complexity of the implementation and exposes users to a simple tool that allows them to accomplish their goals quickly. At the same time, the overall architecture design is reasonable, the scalability is high, and the difficulty of development and expansion is not high. A large number of missing functions are made up through the community, so that Webpack can be qualified for almost any scenario.