Requirejs file loading and dependency management is really good, I believe we all have this experience. Until then, our HTML file header always had a long list of script tags to introduce JS files, and we had to pay great attention to the order of the script tags.
This article has made a simple implementation of REQUIREJS's core functions, hoping to help you understand Requirejs better.
The following idea is that I have made reference to the version of Requirejs 0.0.7. Before trying to understand the current version of the Requirejs source code, but finally found that this special is not a short time to make the fixed. Helpless to find the earlier version of GitHub, when there are not so many configuration items, the code structure is simpler.
--------------------Boom----------------
First, suppose we have such a file structure
Js/require.js
Js/main.js
Js/a.js js/b.js js/a1.js js/a2.js js/b1.js js/b2.js
Index.html
Our portal file is Main.js, and in the portal file we call the Require function
Require (["A", "B"],function(A, b ) {// do something.});
We see in the Require function above that the execution of the rollback function depends on a and B two modules
And then our a.js file looks like this.
Define ("A", ["A1", "A2"],function(A1,B1) { //dosomething});
You can see that the a module relies on the A1,A2 module.
A1 modules like this
Define ("A1",function() { //dosomething});
Similarly, the B module relies on the B1,B2 module, which has similar file structure.
------------------Boom--------------
First talk about the relationship between require and define functions.
The Require and define functions receive the same arguments, but the define function is suggested to be used once in a file to define the module.
The Require function is typically used in portal files or top-level code to load and use modules.
In my opinion, the Require function can be seen as a special define function, which is used to define a top-level anonymous module that does not need to be loaded by other modules.
What are the differences between the two? Here are some descriptions of define and require in Requirejs and how are they used?
Execution flow in the Requirejs
One, Requriejs first find the Data-main property and then load and parse the portal file based on the attribute value (by creating a new script tag).
Here's a look at the Require (["A", "B"],function () {}) call in the portal file what happened?
Two, in the Require function, we are a simple module object, presumably this
{modulename: "[Email protected]$1", deps:["a", "B"],callback:function () {},callbackreturn:null,args:[]}
An explanation of the properties of this module object:
ModuleName: module name. As we said earlier, the Require function can be seen as defining an anonymous top-level module object. So this generates an internal name "[Email protected]$1"
Deps: dependent array; Contains modules that are dependent on the current module.
Callback: Callback function. The callback function in the Require.
Callbackreturn: Callback function return value (in fact, it does not seem to need this property, I mainly consider the use of this property to store the return value of the module callback function, so that when we rely on this module more than once, we can directly return this value. )
Args: An array that corresponds to the value passed back by the dependent module
We are setting a context object globally
Context == ""; // stores the name of the top-level module object generated by the Requre function call. // storage of all modules. Using the module name as the key, the module object as the value// loaded module (loading good refers to the module where the file loaded successfully)= []; modules waiting for loading to complete
We are setting up here
Context.topmodule = "[Email protected]$1"; Because a top-level anonymous module is currently defined, an internal module name is generated.
context.modules Add [email protected]$1 module, results like this
{"[email protected]$1": {modulename: "[Email protected]$1", deps:["a", "B"],callback:function() {}, Callbackreturn:null, args:[]}}
Add dependent modules to the context.waiting, and the results are like this ["a", "B"]; Add dependent modules to waiting (in fact, it can also be optimized to determine if a dependent module already exists in context.loaded)
Then we iterate over the dependent array ["A", "B"], create the script tag and load it, bind the Data-modulename property, and load the completion callback function onscriptloaded, probably like this in the traversal
var script = document.createelement ("script"// the callback function after the scripts are loaded.) It's a core function . Script.setattribute (///// Add Data-modulename property to the script element to make it easy to judge the current module in the callback function = "Js/a.js "; document.getElementsByTagName ("Head") [0].appendchild (script);
Here the Require function is done.
Three. Assuming the above js/a.js is loaded, the file is executed
Define ("A", ["A1", "A2"],function(a1,b1) { //do something});
Let's see what we did in define. In fact, the Define function and the above require function do almost the same thing, the difference is that require automatically generated a module name. And the Context.topmodule is set in the Require.
Build Module {modulename: "a", deps:["A1", "A2"],callback:function () {},callbackreturn:null,args:[]}
Modifying Global context variables
Context.modules the current module is added, the results are as follows
{
"[Email protected]$1": {modulename: "[Email protected]$1", deps:["a", "B"],callback:function() {}, Callbackreturn:null, args:[]},
"A": {modulename:"A", deps:["A1", "A2"],callback:function() {},callbackreturn:null , args:[]}}
Context.waiting adds the current dependent array. -Results ["A", "B", "A1", "B1"]
then create a script tag based on the dependent array , bind the Data-modulename property, bind the callback function onscriptloaded
Four. The last key function onscriptloaded
function onscriptloaded (event) {
That's probably the idea.
1. According to the event object we can get the script element that is loaded and get its Data-modulename property, which is the module name
2. In the global context object, add the module name to the context.loaded array. Subtract the module name from the context.waiting array.
3. Next, if the context.waiting array is not empty, return.
4. Otherwise, if context.waiting is an empty array, it indicates that all dependencies have been loaded.
The next thing is the plays.
5. Create a recursive function to execute the module callback function, like this
functionEXEC (module) {varDeps = Module.deps;//array of dependencies for the current module varargs = Module.args;//callback function parameters for the current module for(vari = 0, len = deps.length; i < Len; i++) {//Traverse varDEP =Context.modules[deps[i]]; Args[i]= EXEC (DEP);//recursion Gets the return value of the dependent module as the corresponding parameter } returnModule.callback.apply (module, args);//call the callback function and pass it to the parameter corresponding to the dependent module. }varTopmodule = Context.modules[context.topmodule];//Locate the top-level module. EXEC (Topmodule);//start the execution of the recursive function
}//onscriptloaded End
The whole idea of implementation is that when we define modules in define and require, all the dependent module names are added to the context.waiting array. Each dependency is bound to the OnLoad event at the time the script tag is loaded. In the event callback function we remove the current module name from Context.waiting, and then we determine whether the context.waiting is empty, which means that all of the module's files are loaded, so you can start with the top-level module and use a recursive function to execute the module's callback function.
At last
I was just trying to write a core idea, so many places in the code are worth pondering, probably not right, but the whole idea is right.
Note that here I am using the Define function, the module name parameter I did not omit, this is because, in the implementation of this piece of thinking, I do not have more space to explain how to implement the Define function omitted module name. Probably the idea is that when define executes, we don't know the module name of the module that is currently defined, so we create a temporary module name and then set a variable temp in the global to point to the module. Given that the Define function executes, the OnLoad event of its script tag is bound to trigger immediately, and the script tag has data-modulename bound to the correct module name. So we can find the module that the temp points to in the OnLoad event callback function, and then modify its module name.
Originally prepared to write an explanation about Requirejs API, finally found that their ink is limited, a lot of things imaginative achievement here can not explain, and finally gave up. If you have any good comments and suggestions about this article, please discuss with me and we will refine this article together.
Requirejs module loading and dependency mechanism analysis and simple implementation.