1. Flow chart of Webpack operation
Follow the flow chart to understand the compilation process
2. Research the benefits of webPack source code
- Understand the running process of Webpack
- Understand the WebPack plug-in
- Learn tips and ideas from Webpack
3. The webpack plug-in
A simple plug-in
class TestPlugin {
apply(compiler) {
console.log("compiler", compiler);
compiler.hooks.compilation.tap("TestPlugin", function() {
console.log("compilation", compilation);
});
}
}
module.exports = TestPlugin;
Copy the code
- The key to write plug-ins: understand what compile, compilation is to do, understand the parameters of these two functions, methods, operation mechanism, to write plug-ins
4. A webpack entrance
-
Webpack starts with the entry, what goes in and what goes out?
- Input: configuration file + source code
- Output: The packaged file
-
Webpack entry is a webpack function, let’s see what this function does, the main flow is as follows:
Webpack = options => {// 1. Integrate options // options is a configuration, including its own configuration and webpack default configuration, guide webpack subsequent run // 2. Instantiate compile const compile = new compile (options.context); // 3. Instantiate all plug-ins and call their apply method. Return compile example return compile; };Copy the code
- The following simulates the code in Webpack-CLI, equivalent to typing webpack on the command line
Const options = require("./webpack.config.js"); // All configuration files const compile = webpack(options); // Execute the webpack function compile.run(); / / executionCopy the code
- Example code for step 3 of the Webpack function
/ / plug-in is instantiated, can be controlled, as shown in the following code / / WebpackOptionsApply js / / options. Optimization. RemoveAvailableModules is true, Will instantiate the plug-in if (options. Optimization. RemoveAvailableModules) {const RemoveParentModulesPlugin = require("./optimize/RemoveParentModulesPlugin"); new RemoveParentModulesPlugin().apply(compiler); }Copy the code
// For some plug-ins, it is necessary to instantiate NodeEnvironmentPlugin, after instantiating compile, New NodeEnvironmentPlugin(compile).run() is executed immediately. class NodeEnvironmentPlugin { apply(compile) { compiler.inputFileSystem = new CachedInputFileSystem( new NodeJsInputFileSystem(), 60000 ); const inputFileSystem = compiler.inputFileSystem; compiler.outputFileSystem = new NodeOutputFileSystem(); compiler.watchFileSystem = new NodeWatchFileSystem( compiler.inputFileSystem ); compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => { if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge(); }); }} // This plugin gives compile object file storage capability, which is a must pluginCopy the code
There are two kinds of Webpack plug-ins, one is required, and the other is optimizedCopy the code
5. compile
From the webpack function, we know that the compile. Run method is executed after the compile object is instantiated. Let’s look at the compile function
// compile. Js class compile extends Tapable {constructor(context) {// Register hook this.hooks = {... beforeRun, run }; } run() { this.hooks.beforeRun.callAsync(this, err => { this.hooks.run.callAsync(this, err => { this.compile(onCompiled); OnCompiled callback}); }); }}Copy the code
Step beforeRun(this hook is registered with NodeEnvironmentPlugin above) => run => calls the compile function, passing in the onCompiled callbackCopy the code
Class compile extends Tapable {createCompilation() {return new Compilation(this); // instantiate Compilation} newCompilation(params) {const Compilation = this.createcompilation (); this.hooks.thisCompilation.call(compilation, params); / / this.hooks.com pilation thisCompilation hook. Call (compilation, params); // thisCompilation hook return compilation; } compile(callback) { this.hooks.beforeCompile.callAsync(params, err => { this.hooks.compile.call(params); const compilation = new newCompilation(params); this.hooks.make.callAsync(compilation, err => { compilation.finish(err => { compilation.seal(err => { this.hooks.afterCompile.callAsync(compilation, err => { return callback(null, compilation); }); }); }); }); }); }}Copy the code
BeforeCompile hook => compile hook => Instantiate compilation(1. ThisCompilation => 2.compilation) => make hook => finish => seal encapsulate => afterCompile hookCopy the code
const onCompiled = (err, compilation) => {
if (this.hooks.shouldEmit.call(compilation) === false) {
this.hooks.done.callAsync(status, err => {});
return;
}
this.emitAssets(compilation, err => {
this.hooks.done.callAsync(status, err => {});
return;
});
};
Copy the code
// Step: shouldEmit? Call emitAssets on success => Done hook, complete one output: call done hook on compile failureCopy the code
- The compile summary
- The call stack of compile is as follows: run => compile => onCompiled
- The run function triggers hooks: beforeRun, run
- The compile function triggers hooks: beforeCompile, compile, thisCompilation, compilation, make, afterCompile
- The onCompiled function triggers hooks: should-emit, emit, done
6. compilation
- Object responsibilities: Build modules and chunks, and optimize the build process with plug-ins
- Let’s start by understanding the relationships between compilation objects, moduleFactory, and Module
Compilation An entry to the compilation module
The compiler’s make hook is instantiated without doing anything to the compilation object. Instead, it calls the make hook directly in the plugins that the hook is mounted to. After the compilation, let’s take a look:
class SingleEntryPlugin { constructor(context, entry, name) { this.context = context; this.entry = entry; this.name = name; } apply(compiler) { compiler.hooks.compilation.tap( "SingleEntryPlugin", (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set( SingleEntryDependency, normalModuleFactory ); }); compiler.hooks.make.tapAsync( "SingleEntryPlugin", (compilation, callback) => { const { entry, name, context } = this; const dep = SingleEntryPlugin.createDependency(entry, name); compilation.addEntry(context, dep, name, callback); }); } static createDependency(entry, name) { const dep = new SingleEntryDependency(entry); dep.loc = { name }; return dep; }}Copy the code
Here we use the SingleEntryPlugin as an example, which is used when a single entry is configured. The plugin attaches the callback function to the make hook. This hooks not only mount the make hook, but also the Compilation hook. This hook is called before the make hook and adds a value to the Compilation object’s dependencyFactories. This value is a key-value pair. The key is SingleEntryDependency and the value is normalModuleFactory. NormalModuleFactory is a moduleFactory that we will use later in our build module.
Compilation steps are as follows:
“Make hook” => Compilation => addEntry => _addModuleChain => Compile.seal The seal method calls various hooks, generates chunk object => control to compile, executes onCompiled => callback to call done hook, and the build is completed in one time
7. Modify the code and compile again
When we modify the business code to compile again, webpack-CLI will call compile.run() again, and the webpack function will not be called again