I analyzed the require.js source code some time ago. The overall analysis was a bit general, and the content was a bit empty and unclear. The essence of requirejs dependency processing, this is the most failed part in analyzing the require.js source code.
Although I don’t really understand its implementation details, I have an overall understanding of its source code organization and basic logic execution.
This article refers to the source code on the Internet and analyzes the simple module loader implemented by its ideas, aiming to deepen the knowledge and understanding of require.js.
The first clear points are as follows:
Every time the require function is called, a Context will be created Object
Module object represents the module object, and the basic operations are the prototype methods of the object
The above two objects are to implement this simple module The loader core, the processing flow during the loading process is shown in the figure below:
The properties of the Module object are:
mid: Indicates module id
src: module path
name: module name
deps: module dependency list
callback: callback function
errback: error handling function
status: module status
exports: module output corresponding to the callback function parameter sequence
The prototype object of Module has the following methods:
init: Initialization processing of Module object
fetch: Create a script node and append it to the element node
checkCycle: handles cyclic dependencies and returns the current cyclic dependency list
handleDeps: handle dependency list
changeStatus: Change the status of the module, mainly processing whether the module is loaded successfully
execute: After all dependent modules are loaded successfully, get the parameter list
In fact, processing the dependency list is define and require In processing, the processing codes of define function and require function are as follows:
First look at the require function, example:
require(['a', 'b'], function(a, b) { console.log(a, b); });
As can be seen from the above, calling the require function depends on The list is ['a', 'b'], and the callback function is function(a, b) {console.log(a, b);}
As can be seen from the require code, the Context constructor is called Create a Context object, now look at the processing of the Context constructor: let Context = function(deps, callback, errback) {
this.cid = ++contextId; this.init(deps, callback, errback); } ; Context.prototype.init = function(deps, callback, errback) { this.deps = deps; this.callback = callback; this.errback = errback; contexts[this.cid] = this; } ;
The important thing in the above is contexts[this.cid] = this; Register the current Context object into the global contexts object collection.
Then call the handleDeps function, which processes the dependency list. The specific code is as follows: handleDeps: function() {
let depCount = this.deps ? this.deps.length : 0; // require.js中处理循环依赖的处理 let requireInDep = (this.deps || []).indexOf('require'); if (requireInDep !== -1) { depCount--; this.requireInDep = requireInDep; this.deps.splice(requireInDep, 1); } // 处理循环依赖情况 let cycleArray = this.checkCycle(); if (cycleArray) { depCount = depCount - cycleArray.length; } // depCount表示当前模块的依赖模块数,depCount为0表示模块中某一依赖加载完成 this.depCount = depCount; if (depCount === 0) { this.execute(); return; } // 遍历依赖列表,创建Module对象,并且将当前模块与其依赖的关系构建出来maps this.deps.forEach((depModuleName) => { if (!modules[depModuleName]) { let module = new Module(depModuleName); modules[module.name] = module; } if (!maps[depModuleName]) { maps[depModuleName] = []; } maps[depModuleName].push(this); } );
}Handling of circular dependencies
The processing of circular dependencies in the implementation code this time is the official method in require.js, which is to pass require and require again in the callback function. Why can this solve the circular dependencies?
As far as this implementation is concerned, require will create a Context object once. The main code is as follows: // Processing of circular dependencies in require.js let requireInDep = (this.deps || []).indexOf('require');
if (requireInDep !== -1) { depCount--; this.requireInDep = requireInDep; this.deps.splice(requireInDep, 1); } // 获取循环依赖 let cycleArray = this.checkCycle(); if (cycleArray) { depCount = depCount - cycleArray.length; } // execute函数中代码// 插入require到回调函数的参数列表中if (this.requireInDep !== -1 && this.requireInDep !== undefined) { arg.splice(this.requireInDep, 0, require); }
Conclusion
What I have learned on paper is shallow, and I realize that this matter must be carried out. By implementing a simple module loader, the idea and logical processing of require.js module loading become clearer.
Although require.js handles asynchronous loading of js files differently, the essence is the same. require.js adds a script node to the head tag, and the script adds the async attribute to achieve asynchronous loading. .
The above is the detailed content of How to implement a simple module loader using js. For more information, please follow other related articles on the PHP Chinese website!