Javascript Framework Design Reading Notes module loading system _ javascript skills

Source: Internet
Author: User
Tags define definition
This article is the Reading Notes of the module Loading System in the second chapter of situ zhengmei's javascript framework design. Based on my understanding, I will briefly introduce the main content of this chapter to you, this makes it easier for everyone to learn. Module loading is actually to divide js into many modules for development and maintenance. Therefore, when loading many js modules, dynamic loading is required to improve the user experience.

Before introducing the module to load databases, we should first introduce a method.

Dynamic js loading method:

The Code is as follows:


Function loadJs (url, callback ){
Var node = document. createElement ("script ");
Node [window. addEventListener? "Onload": "onreadystatechange"] = function (){
If (window. addEventListener |/loaded | complete/I. test (node. readyState )){
Callback ();
Node. onreadystatechange = null;
}
}
Node. onerror = function (){};
Node. src = url;
Var head = document. getElementsByTagName ("head") [0];
Head. insertBefore (node, head. firstChild); // insert it to the first node of the head to prevent the head label under ie6 from being closed and use appendChild to report an error.
}

Situ zhengmei uses its mass framework to introduce module loading, while require. js and sea. js are the most widely used in the industry. Therefore, I think he has a strong personality.

Let me talk about the module loading process of sea. js:

On the chaojidan. jsp page, introduce sea. js in the head tag, and you will get the seajs object.

Index. js is also introduced.

The index. js code is as follows:

The Code is as follows:


Seajs. use (['./A', 'jquery'], function (a, $ ){
Var num = a.;
$ ('# J_A'). text (num );
})

A. js:

The Code is as follows:


Define (function (require, exports, module ){
Var B = require ('./B ');
Var a = function (){
Return 1 + parseInt (B. B ());
}
Exports. a =;
})

B. js:

The Code is as follows:


Define (function (require, exports, module ){
Var c = require ('./C ');

Var B = function (){
Return 2 + parseInt (c. c ());
}
Exports. B = B;
})

C. js:

The Code is as follows:


Define (function (require, exports, module ){
Var c = function (){
Return 3;
}
Exports. c = c;
})

We can see that module a depends on Module B and Module B depends on Module c.

When the program enters index. js, seajs will call the use method.

The Code is as follows:


Seajs. use = function (ids, callback ){
GlobalModule. _ use (ids, callback)
}

Note: When globalModule is seajs initialization (when sea. js is introduced), Module instance var globalModule = new Module (util. pageUri, STATUS. COMPILED)
Ids-> ['. /A', 'jquery '], callback-> function (a, $) {var num =. a; $ ('# J_A '). text (num );}

Next, we will call globalModule. _ use (ids, callback)

The Code is as follows:


Module. prototype. _ use = function (ids, callback ){
Var uris = resolve (ids, this. uri); // parse ['./A', 'jquery']
This. _ load (uris, function () {// call the _ load method to resolve the address of the and jquery modules [url1, url2.
// Util. map: enables all data members to execute a specified function at a time, and returns a new array, which is the result of the callback of the original number of members.
Var args = util. map (uris, function (uri ){

Return uri? CachedModules [uri]. _ compile (): null; // If a url exists, call the _ compile method.
})
If (callback) {callback. apply (null, args )}
})
}

Because two callback functions will appear after the _ load method is called, we set function (a, $) {var num =. a; $ ('# J_A '). text (num);} indicates callback1,
Mark this. _ load (uris, function () {}) as callback2.
The resolve method is used to parse the module address. I will not elaborate on it here.
Var uris = resolve (ids, this. URL in uri) is parsed into ['HTTP: // localhost/test/SEAJS/. js ', 'HTTP: // localhost/test/SEAJS/lib/juqery/1.7.2/juqery-debug.js'], the module path resolution has been completed.

Next, we will execute this. _ load

The Code is as follows:


// The _ load () method first determines which resource files do not have ready. If all resource files are in ready status, run callback2.
// Here, we will also judge the circular dependency and load the js that is not loaded.
Module. prototype. _ load = function (uris, callback2 ){
// Util. filter: enables all data members to execute a specified function at a time, and returns a new array. This array is a member of the original number group after performing a callback.
// UnLoadedUris is the uri array of uncompiled modules.
Var unLoadedUris = util. filter (uris, function (uri ){
// Returns the Member whose Boolean value is true. If the uri exists and the internal variable cacheModules does not exist or its status value is smaller than STATUS. READY in the stored information, true is returned.
// The value of STATUS. READY is 4. If the value is smaller than four, it is possible that it is being obtained and downloaded.
Return uri &&(! CachedModules [uri] |
CachedModules [uri]. status <STATUS. READY)
});
// If all modules in uris are ready for ready, execute the callback and exit the function body (then the _ compile method of the module will be called ).
Var length = unLoadedUris. length
If (length = 0) {callback2 () return}
// Number of modules not loaded
Var remain = length
// Create a closure and try to load those modules that are not loaded
For (var I = 0; I <length; I ++ ){
(Function (uri ){
// Determine if the storage information of this uri does not exist in the internal variable cachedModules, instantiate a Module object.
Var module = cachedModules [uri] |
(CachedModules [uri] = new Module (uri, STATUS. FETCHING ))
// If the status value of the module is greater than or equal to 2, it means that the module has been downloaded and already exists locally. At this time, execute onFetched ()
// Otherwise, call fetch (uri, onFetched) to download the resource file. The onload is triggered after the resource file is downloaded, And the onload method is called back to onFetched.
Module. status> = STATUS. FETCHED? OnFetched (): fetch (uri, onFetched)
Function onFetched (){
Module = cachedModules [uri]
// When the module's STATUS value is greater than or equal to STATUS. SAVED, it means that all the dependency information of the module has been obtained
If (module. status> = STATUS. SAVED ){
// GetPureDependencies: obtains the dependency array without circular dependency.
Var deps = getPureDependencies (module)
// If the dependent array is not empty
If (deps. length ){
// Execute the _ load () method again until all dependencies are loaded.
Module. prototype. _ load (deps, function (){
Cb (module)
})
}
// If the dependent array is empty, run cb (module) directly)
Else {
Cb (module)
}
}
// If the retrieval fails, such as 404 or does not comply with the modular Specification
// In this case, module. status is maintained at FETCHING or FETCHED.
Else {
Cb ()
}
}
}) (UnLoadedUris [I])
}
// Cb method-call back after all modules are loaded
Function cb (module ){
// If the storage information of the module exists, modify the status value in the storage information of the module to STATUS. READY.
Module & (module. status = STATUS. READY)
// The callback is executed only when all modules are loaded.
-- Remain = 0 & callback2 ()
}
}
}

Here the length of the unLoadedUris array is 2, ['HTTP: // localhost/test/SEAJS/. js', 'HTTP: // localhost/test/SEAJS/lib/juqery/1.7.2/juqery-debug.js '], so two closures named by the js path will be generated.

Take http: // localhost/test/SEAJS/a. js as an example.
Next, a Module is created:

The Code is as follows:


CachedModules ('HTTP: // localhost/test/SEAJS/a. js') = new Module ('HTTP: // localhost/test/SEAJS/a. js', 1)
Module. status> = STATUS. FETCHED? OnFetched (): fetch (uri, onFetched)

Because module a is not loaded at this time, fetch (uri, onFetched) will be executed next, that is, fetch ('HTTP: // localhost/test/SEAJS/a. js', onFetched ).

The Code is as follows:


Function fetch (uri, onFetched ){
// Replace the uri with the new request address according to the rules in the map.
Var requestUri = util. parseMap (uri)
// First, check whether the obtained list contains the requestUri record.
If (fetchedList [requestUri]) {
// At this time, refresh the module storage information of the original uri to the requestUri that is redefined by map.
CachedModules [uri] = cachedModules [requestUri]
// Execute onFetched and return, which means the module has been obtained successfully.
OnFetched ()
Return
}
// Query the storage information of requestUri In the retrieved list
If (fetchingList [requestUri]) {
// Add the callback corresponding to the uri to the callbacklist and return
CallbackList [requestUri]. push (onFetched) // if it is being obtained, push the onFetched callback method of this module into the array and return it.
Return
}
// If the modules to be retrieved are not listed in the fetchedList and fetchingList, add the information in the request list and callback list respectively.
FetchingList [requestUri] = true
CallbackList [requestUri] = [onFetched]
// Fetches it
Module. _ fetch (
RequestUri,
Function (){
FetchedList [requestUri] = true
// Updates module status
// If module. status is equal to STATUS. FECTCHING, modify the module status to FETCHED.
Var module = cachedModules [uri]
If (module. status = STATUS. FETCHING ){
Module. status = STATUS. FETCHED
}
If (fetchingList [requestUri]) {
Delete fetchingList [requestUri]
}
// Call back the callbackList
If (callbackList [requestUri]) {
Util. forEach (callbackList [requestUri], function (fn ){
Fn () // fn is the onFeched method corresponding to module.
})
Delete callbackList [requestUri]
}
},
Config. charset
)
}

The Module. _ fetch () will be executed. The callback function here is called callback3.

This method is to call the loadJs method to dynamically download the. js file. (Because there are a and jquery, two scripts will be created.) There is a question: Create a script and add it to the head. Then, the js file will be downloaded, however, in seajs, the file is not downloaded. Instead, the file will be downloaded only after the jquery script is created and added to the head. (The Google debugger sets the breakpoint and the pending is always displayed ). Is this for Mao?
(We recommend you refer to the following link for more information: callback. At the same time, the Midea e-commerce interviewer told me that the table will be displayed only after it is completely parsed, and the p resolution will be displayed as much as it is. It is verified that if the table contains a tbody tag, it will be displayed in parts according to the tbody. Therefore, in IE6, 7, 8, if you use innerHTML to create"

", Will automatically add.).
After the download is successful, it will parse and execute the define method. The Code of module a will be executed first.
Define (id, deps, function () {}) method Parsing

The Code is as follows:


// Define definition, id: module id, deps: module dependency, factory
Module. _ define = function (id, deps, factory ){
// Parse dependency // If deps is not an array type and factory is a function
If (! Util. isArray (deps) & util. isFunction (factory) {// The regular expression matches the require string in the function body and returns an array and assigns a value to deps.
Deps = util. parseDependencies (factory. toString ())
}
// Set the metadata
Var meta = {id: id, dependencies: deps, factory: factory}
If (document. attachEvent ){
// Obtain the node of the current script
Var script = util. getCurrentScript ()
// If the script node exists
If (script ){
// Obtain the original uri address
DerivedUri = util. unParseMap (util. getScriptAbsoluteSrc (script ))}
If (! DerivedUri ){
Util. log ('failed' to derive URI from interactive script for: ', factory. toString (), 'warn ')
}
}
.........
}

Define will first execute a judgment on the factory to determine whether it is a function (because define can also include files and objects)

If it is a function, the function will be obtained through factory. toString (), and the dependencies of a. js will be matched through regular expressions, and the dependencies will be saved in deps.

For a. js, it depends on B. js, so deps is ['./B']

Save the information of a. js var meta = {id: id, dependencies: deps, factory: factory}

For a. js meta = {id: undefined, dependencies: ['./B'], factory: function (xxx) {xxx }}

In ie 6-9 browser, you can get the path for running js, but in standard browser, this is not feasible. Therefore, assign the meta information to anonymousModuleMeta = meta for the moment.

Then, the onload is triggered, and the callback method callback3 is called. The callback method modifies the current callback module (. js), set it to module. status = STATUS. FETCHED.

Next, the callback corresponding to a. js in the callback queue callbackList will be uniformly executed, that is, onFetched.

The onFetched method checks whether module a depends on the Module B. Because module a depends on Module B, it executes _ load () on the B. js dependency of module ().

Will download Module B, then the define method of jquery will be executed first. Because jquery does not rely on modules, after the onload callback. OnFetched calls the cb method.

After B implements the same process as a, it downloads the c module. In the end, all modules c, B, And a download and execute define. After onload is completed, the cb method will also be called (c first, B, and c later)

After all modules are ready, the callback2 method is called.
Call back callback2 and execute the _ compile method of the and jquery modules:

First, compile the function execution of the. js module and module a. Because require (B. js) exists in module a, the function of Module B will be executed.
Function of module a is executed.
Function of Module B is executed.
Function of Module c is executed.
Function execution of Module c is complete.
Module B's function execution is complete
Module a's function execution is complete

Finally, run the jquery function.

After compilation, run callback1 to use the and jquery objects.

PS: The seajs version has been updated and there is no _ compile method. (You can go and see it on your own)

Next, let's talk about the module compilation _ compile process of seajs.

First, compilation of a. js

The Code is as follows:


Module. prototype. _ compile = function (){
126 var module = this
127 // If the module has been compiled, the module. exports is returned directly.
128 if (module. status = STATUS. COMPILED ){
129 return module. exports
130}
133 // 1. the module file is 404.
134 // 2. the module file is not written with valid module format.
135 // 3. other error cases.
136 // handle some exceptions. In this case, null is returned directly.
137 if (module. status <STATUS. SAVED &&! HasModifiers (module )){
138 return null
139}
140 // change the module status to COMPILING, indicating that the module is being compiled
141 module. status = STATUS. COMPILING
142
143 // used internally by the module. It is a method used to obtain interfaces provided by other modules (called submodules) for synchronous operations.
144 function require (id ){
145 // path of the parsing module based on id
146 var uri = resolve (id, module. uri)
147 // obtain the module from the module cache (note that the sub-module as the main module dependency has been downloaded here)
148 var child = cachedModules [uri]
149
150 // Just return null when uri is invalid.
151 // if child is null, it can only indicate that the uri is incorrect due to an error in parameter filling. Then null is returned directly.
152 if (! Child ){
153 return null
154}
155
156 // Avoids circular CILS.
157 // If the sub-module is in the STATUS of STATUS. COMPILING, the child. exports is returned directly to avoid repeated compilation of modules due to loop dependency.
158 if (child. status = STATUS. COMPILING ){
159 return child. exports
160}
161 // point to the module that calls the current module during initialization. Based on this attribute, you can obtain the Call Stack when the module is initialized.
162 child. parent = module
163 // return the compiled child module. exports
164 return child. _ compile ()
165}
166 // used internally by the module to asynchronously load the module and execute the specified callback after loading.
167 require. async = function (ids, callback ){
168 module. _ use (ids, callback)
169}
170 // use the path resolution mechanism inside the module system to parse and return the module path. This function does not load modules and only returns the parsed absolute path.
171 require. resolve = function (id ){
172 return resolve (id, module. uri)
173}
174 // you can view all modules loaded by the module system.
175 // in some cases, if you need to re-load a module, you can obtain the uri of the module and delete the module information by using delete require. cache [uri. In this way, it will be retrieved again next time.
176 require. cache = cachedModules
177
178 // require is a method used to obtain interfaces provided by other modules.
179 module. require = require
180 // exports is an object used to provide module interfaces.
181 module. exports = {}
182 var factory = module. factory
183
184 // when factory is a function, it indicates the construction method of the module. Run this method to obtain the interface provided by the module.
185 if (util. isFunction (factory )){
186 compileStack. push (module)
187 runInModuleContext (factory, module)
188 compileStack. pop ()
189}
190 // when factory is an object, string, or other non-function type, it indicates that the module interface is equivalent to this object and a string.
191 // For example, define ({"foo": "bar "});
192 // For example: define ('I am a template. My name is {name }}.');
193 else if (factory! = Undefined ){
194 module. exports = factory
195}
196
197 // change the module status to COMPILED, indicating that the module has been COMPILED
198 module. status = STATUS. COMPILED
199 // execute the Module Interface modification through seajs. modify ()
200 execModifiers (module)
201 return module. exports
202}

The Code is as follows:


If (util. isFunction (factory )){
186 compileStack. push (module)
187 runInModuleContext (factory, module)
188 compileStack. pop ()
189}

The module. export is initialized here. RunInModuleContext method:

The Code is as follows:


// Execute the module code according to the module Context
489 function runInModuleContext (fn, module ){
490 // pass in two parameters related to the module and the module itself
491 // exports used to expose the interface
492 // require is used to obtain the dependency module (synchronous) (Compiled)
493 var ret = fn (module. require, module. exports, module)
494 // supports the interface form for exposing returned values, such:
495 // return {
496 // fn1: xx
497 //, fn2: xx
498 //...
499 //}
500 if (ret! = Undefined ){
501 module. exports = ret
502}
503}

Execute the function Method in a. js and call var B = require ("B. js? 1.1.15 "),
The require method returns the return value of B's compile method, and var c = require ('C. js') exists in Module B ').
In this case, the compile method of c is called, and then the function of c is called. In c, if you want to expose an object or return object c, the exports of Module c is c. Or directly the module. export = c; in short, module c is returned. export = c; so var c = module c. export = c. In Module B, you can use variable c to call the methods and attributes of the c object in Module c.
In the end, module a can also call the attributes and methods of object B in Module B.
No matter what module, as long as module. export = xx module is used, other modules can use require ("xx module") to call various methods in the xx module.
The final module status changes to module. STATUS = status. COMPILED.

The Code is as follows:


Module. prototype. _ use = function (ids, callback ){
Var uris = resolve (ids, this. uri); // parse ['./A', 'jquery']
This. _ load (uris, function () {// call the _ load method to resolve the address of the and jquery modules [url1, url2.
// Util. map: enables all data members to execute a specified function at a time, and returns a new array, which is the result of the callback of the original number of members.
Var args = util. map (uris, function (uri ){

Return uri? CachedModules [uri]. _ compile (): null; // If a url exists, call the _ compile method.
})
If (callback) {callback. apply (null, args )}
})
}

In this case, args = [module a. export, module jquery. export];

The Code is as follows:


Seajs. use (['./A', 'jquery'], function (a, $ ){
Var num = a.;
$ ('# J_A'). text (num );
})


In this case, a and $ in function are module a. export and module jquery. export.

Because I am studying jquery source code and jquery framework design, I share some experience:
Jquery source code, I have read a lot of analysis on the internet, and I can't watch it anymore. It is of little significance. We recommend jquery source code parsing in the wonderful class.

Situ's javascript framework design is difficult, but after intensive reading, you are a senior front-end engineer.

Yuber's sea. js, I suggest learning and using it. After all, it is made by the Chinese themselves. Our company uses seajs for new projects or reconstruction.

The next step is the intensive reading of the modular handbars and mvc backbone or mvvm angular source code. Here, I want someone to give me some advice on what books to read, what websites to watch, and what videos to learn quickly.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.