Abstract

The core function of Webpack is realized by pulling out a lot of plug-ins, so the division of functions in the system is very fine, so it achieves perfect decoupling and clear division of labor, and the code is easy to maintain. Plug-ins are the cornerstones of WebPack, and these cornerstones influence the direction of the process. These hooks are strung together by Tapable, which is analogous to the Vue framework lifecycle. Webpack also has its own lifecycle, in which hooks are sequentially triggered and plugins mounted on these hooks are executed to perform specific logic processing. Within plug-ins, the built entities or built data results are reachable, which makes WebPack highly extensible. With that in mind, there is no doubt about how WebPack has such a rich ecosystem and community, and how it got to where it is today.

About the Compiler

Compiler objects are webpack entities (Tapable instances) that control the life cycle of the webpack. They do not perform specific tasks, but only perform some scheduling work (dispatching troops). He creates the Compilation object, which returns the final results to Compiler when the Compilation task is completed. The official website lists all the hooks exposed by this object.

About the Compilation

Compilation is the main performer of the Compilation phase (an instance of Tapable), an object that performs the main tasks of module creation, dependency collection, chunking, and packaging. The official website lists all the hooks exposed by this object.

In Chapter 2, we learned that after obtaining configuration data, the compiler.run method was launched. In fact, before compiler starts RUN, we can find many initialization operations in webpack.js, such as adding default operations, and initializing corresponding plug-ins for different configuration items (such as target and devtool).

Webpack supports passing in multiple configuration objects. For example, if a library has multiple build targets, it needs to pass in multiple configuration objects, each of which will be executed. If you pass in an array, instead of the Compiler, you initialize the MultiCompiler, and the run method on the MultiCompiler runs through the compilers object that stores the Compiler array. Conmpiler’s run method is then called in turn.

Compiler

Compiler is started by the run method, in which two actions are mainly concerned: compile method is called; OnCompiled declares the callback function passed in by compile.Copy the code
  1. compile()

    There are several important hooks involved in the WebPack build lifecycle:

    1. BeforeCompile, compile, make(key hooks), afterCompile, thisCompilation, compilation
    2. In this method, we build the parameters needed to create the compilation and create the compilation. This parameter is the factory function that will be used to parse the Module later. The make hook is a key hook. When called, it passes in a newly created compilation object, attaching some processing logic to the compiler.addentry (). Control passes from the Compiler to Compilation. The Compilation finish and seal hooks are called within the make hook callback.
  2. onCompiled()

    The last few important hooks involved in the WebPack build life cycle: emit, done. This method is equivalent to taking the Compilation permissions back. The compilation object is a compilation of all resource files after module parsing, loader processing, template compilation. This method mainly calls the emitAssets method, which calls the EMIT hook (in this step, we can obtain complete build data), obtains all assets resource data created by compilation, and recursively calls writeOut to write the final chunk file. And calls the done hook.

Compilation

The compilation starts with the addEntry method and ends with addEntry.Copy the code
  1. AddEntry () : indicates a single entry or multiple entry based on the configuration mode of the entry. This method calls _addModuleChain().
  2. _addModuleChain() : Creates module. Depending on the entry, create modules using the create method of a different module factory (retrieved from parameters passed into the creation Compilation, including ContextModuleFactory or NormalModuleFactory). Obtain data information about the module, such as Parser, Loader, and hash.
  3. BuildModule () : Calls module.build() to build modules.
  4. AddModuleDependencies () : After the build succeeds, recursively get the dependency module and build (the same logic as _addModuleChain).
  5. SuccessEntry: After you’ve done that, call the succeedEntry hook in the _addModuleChain callback, where you can retrieve the newly created Module. Control is then returned to the Compiler.

After the above phase is complete, Cimpiler calls the Compilation finish() and seal(), focusing on the seal method.

  1. Seal () : This method basically completes the chunk build. Modules and chunks are collected, and chunks are compiled using templates (MainTemplate, ChunkTemplate, etc.) initialized in the Compilation builder. The entry module configured with the entry property uses the MainTemplate, which adds the code needed to start webpack, create and update hash information, etc., and calls emitAsset() to collect all the final resource files into assets.
  2. EmitAsset () : Collects assets data. This method is called by multiple plug-ins.

Tapable transformation

In order to facilitate the learning of source code, want to get the webpack execution of the hook mount and trigger situation, modified Tapable. The main is to modify the Hook. Js file. The fs library needs to be introduced at the top.

const fs = require('fs')
Copy the code
  1. Insert logic into the call method
_fnCp(fn, name, type) {
      const _fn = (. arg) = > {
            // console.log('hahaha:', arg)
            fs.writeFileSync('/Users/eleme/Documents/my/test-webpack/calls.js'.`${type}: ${name} \n`, { 'flag': 'a'}, () = > {})returnfn(... arg) }return _fn
}
// Modify the tap, tapAsync, tapPromise methods
tap(options, fn) {
      // ...
      // options = Object.assign({ type: "sync", fn: fn }, options);
      options = Object.assign({ type: "sync".fn: this._fnCp(fn, options.name, "sync") }, options);
      // ...
}
tapAsync(options, fn) {
      // ...
      // options = Object.assign({ type: "async", fn: fn }, options);
      options = Object.assign({ type: "async".fn: this._fnCp(fn, options.name, "async") }, options);
      // ...
}
tapPromise(options, fn) {
      // ...
      // options = Object.assign({ type: "promise", fn: fn }, options);
      options = Object.assign({ type: "promise".fn: this._fnCp(fn, options.name, "promise") }, options);
      // ...
}
Copy the code
  1. Insert logic in the TAP method
// Modify the insert method to insert a statement at the end of the method
_insert(item){
      fs.writeFileSync('/Users/eleme/Documents/my/test-webpack/taps.js'.`${item.type}: ${item.name} \n`, { 'flag': 'a'}, () => {})}Copy the code

conclusion

So far, we’ve taken a look at the webPack build context. The WebPack system is very large and internally encapsulates many webPack libraries. The purpose of learning Webpack source code is to learn good construction ideas, one is to facilitate their own business development plug-ins, there are source code annotations and other information for expansion.