This article first appeared on blog.flqin.com. If you have any errors, please contact the author. Analysis code word is not easy, reproduced please indicate the source, thank you!
Initialize the module
Connected, in the resolver function callback, trigger normalModuleFactory. Hooks: after afterResolve callback to perform:
let createdModule = this.hooks.createModule.call(result); // result is the composite object data returned by resolver
if(! createdModule) {if(! result.request) {return callback(new Error('Empty dependency (no request)'));
}
createdModule = new NormalModule(result);
}
createdModule = this.hooks.module.call(createdModule, result);
return callback(null, createdModule);
Copy the code
Here trigger normalModuleFactory. Hooks: createModule, if there is no project configuration in the hook of the custom module, use the webpack generated module.
Get the module instance, and then trigger normalModuleFactory. Hooks: after the module, jump out of the factory function, after implementation of factory function callback depend on the cache, Exit the create function to execute the moduleFactory.create callback. Execute in callback:
const addModuleResult = this.addModule(module); // Save the 'module' to the global 'Compilation. Modules' array and' _modules' object, and check whether '_modules' has a flag for the module to set whether it is loaded or not
module = addModuleResult.module;
onModule(module); // Modules will also be saved to 'compilation. entries' if it is an entry file
dependency.module = module;
module.addReason(null, dependency); // Add which modules depend on this' module '
Copy the code
We then call this.buildModule to get to the build phase. This method made the callback after the cache, trigger compilation. Hooks: buildModule, then execute the module. The build ().
Build the module
In/node_modules/webpack/lib/NormalModule js file execution module. The build, set some attributes, directly call the enclosing doBuild.
This. CreateLoaderContext gets loaderContext, provides context for all loaders to share, and then calls runLoaders:
runLoaders(
{
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: fs.readFile.bind(fs),
},
(err, result) = > {
/ /...});Copy the code
loader-runner
This method comes from loader-runner, which processes the source code through various loaders to get a processed string or buffer (and possibly a sourcemap).
You can also parse custom loaders to write a loader.
The main process is:
RunLoaders -> iteratePitchingLoaders (require each loader in normal order) -> loadLoader (the corresponding loader export function assigned to Loadercontext.loader []. Normal, pitch Function assigned to loaderContext.loader[]. Pitch, Then execute pitch function (if any) -> processResource (convert buffer and set loaderIndex) -> iterateNormalLoaders (execute all loaders in reverse order) -> RunSyncOrAsync (synchronous or asynchronous execution loader)
Pitch function
-
Each loader can mount a pitch function that takes advantage of the Module’s request and does not actually process the Module’s content document.
-
Require loader and execute its pitch method (loadLoader). In the callback after execution, if there are parameters other than err, The iterateNormalLoaders execution passes the remaining unrequired loaders directly to the step of executing the loader. If you want no other parameters, execute iteratePitchingLoaders for the next loader require. As shown in the code:
if (args.length > 0) { loaderContext.loaderIndex--; iterateNormalLoaders(options, loaderContext, args, callback); } else { iteratePitchingLoaders(options, loaderContext, callback); } Copy the code
-
Execute the normal method of each loader in reverse order.
Core code parsing
// This method requires each loader in order
function iteratePitchingLoaders(options, loaderContext, callback) {
// Abort after last Loader The processResource method is executed
if (loaderContext.loaderIndex >= loaderContext.loaders.length) return processResource(options, loaderContext, callback);
var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex]; // Select the first loader
// Iterate recursively reads the next loader after the increment
if (currentLoaderObject.pitchExecuted) {
loaderContext.loaderIndex++;
return iteratePitchingLoaders(options, loaderContext, callback);
}
// Load loader module Loads the Loader module
// The function exported by the corresponding loader is assigned to loaderContext.loader[].normal
loadLoader(currentLoaderObject, function (err) {
if (err) {
loaderContext.cacheable(false);
return callback(err);
}
var fn = currentLoaderObject.pitch; //loadLoader assigns module to loader.normal, pitch to loader.pitch
currentLoaderObject.pitchExecuted = true;
if(! fn)return iteratePitchingLoaders(options, loaderContext, callback);
// If so, start the pitch function and decide whether to continue reading the rest of the loader according to the parameters
runSyncOrAsync(fn, loaderContext, [loaderContext.remainingRequest, loaderContext.previousRequest, (currentLoaderObject.data = {})], function (err) {
if (err) return callback(err);
var args = Array.prototype.slice.call(arguments.1);
if (args.length > 0) {
loaderContext.loaderIndex--;
iterateNormalLoaders(options, loaderContext, args, callback);
} else{ iteratePitchingLoaders(options, loaderContext, callback); }}); }); }// Convert buffer and set loaderIndex
function processResource(options, loaderContext, callback) {
// set loader index to last Loader Obtains the index of the last loader
loaderContext.loaderIndex = loaderContext.loaders.length - 1;
var resourcePath = loaderContext.resourcePath;
if (resourcePath) {
loaderContext.addDependency(resourcePath);
// Convert to buffer
options.readResource(resourcePath, function (err, buffer) {
if (err) return callback(err);
options.resourceBuffer = buffer; //得到buffer
iterateNormalLoaders(options, loaderContext, [buffer], callback);
});
} else {
iterateNormalLoaders(options, loaderContext, [null], callback); }}// Execute all loaders in reverse order
function iterateNormalLoaders(options, loaderContext, args, callback) {
if (loaderContext.loaderIndex < 0) return callback(null, args); // Exit after executing all loaders to execute the iteratePitchingLoaders callback, i.e. the runLoaders callback
var currentLoaderObject = loaderContext.loaders[loaderContext.loaderIndex]; // Obtain the corresponding loader
// Iterate after subtraction executes the next loader recursively
if (currentLoaderObject.normalExecuted) {
loaderContext.loaderIndex--;
return iterateNormalLoaders(options, loaderContext, args, callback);
}
var fn = currentLoaderObject.normal;
currentLoaderObject.normalExecuted = true;
if(! fn) {return iterateNormalLoaders(options, loaderContext, args, callback);
}
convertArgs(args, currentLoaderObject.raw);
// Execute the loader function
runSyncOrAsync(fn, loaderContext, args, function (err) {
// Loader executes the result callback
if (err) return callback(err);
var args = Array.prototype.slice.call(arguments.1); // arg:[] Converts the result for loader (string or buffer+ sourcemap if possible)
iterateNormalLoaders(options, loaderContext, args, callback); // the result of the transformation is passed in
});
}
// Execute pitch/loader functions synchronously or asynchronously
function runSyncOrAsync(fn, context, args, callback) {
var isSync = true;
var isDone = false;
var isError = false; // internal error
var reportedError = false;
// Asynchronous processing
context.async = function async() {
if (isDone) {
if (reportedError) return; // ignore
throw new Error('async(): The callback was already called.');
}
isSync = false;
return innerCallback;
};
// This method is executed after asynchron, and the result of the loader is passed as an argument
var innerCallback = (context.callback = function () {
if (isDone) {
if (reportedError) return; // ignore
throw new Error('callback(): The callback was already called.');
}
isDone = true;
isSync = false;
try {
callback.apply(null.arguments); // arguments is loader result, the first value is null, the second is string or buffer, and the third is SourceMap
} catch (e) {
/ /...}});try {
var result = (function LOADER_EXECUTION() {
return fn.apply(context, args); //*** entry: executes the loader function, passing the previous loader execution result ***}) ();if (isSync) {
isDone = true;
if (result === undefined) return callback();
if (result && typeof result === 'object' && typeof result.then === 'function') {
return result.then(function (r) {
callback(null, r);
}, callback);
}
return callback(null, result); }}catch (e) {
/ /...}}Copy the code
The summary of this chapter
- instantiation
NormalModule
I get initializedmodule
(Method chain:Modulefactory. create callback ->buildModule->module.build->module.doBuild->runLoaders
), and thenbuild
In the process of firstrun loader
Process the source code to get a compiled string orbuffer
. - in
run loader
The procedure is pre – ordered execution of eachloader
的pitch
“, and then executes each in reverse orderloader
的normal
.