seajs1.3.0 Source Analysis module relies on ordered loading _javascript techniques

Source: Internet
Author: User
Tags closure modifier modifiers

Here is the core of Seajs loader, some IE compatible parts are not very clear, mainly understand how each module relies on ordered loading, as well as the CMD specification.

The code is a bit long and needs to be patiently read:

Copy Code code as follows:

/**
* The core of Loader
*/
;(function (seajs, util, config) {
Module caching
var cachedmodules = {}
Interface Modify Cache
var cachedmodifiers = {}
compiling queues
var compilestack = []
Module status
var STATUS = {
' Fetching ': 1,//The module file is fetching now. module is in download
' Fetched ': 2,//The module file has been fetched. Module has been downloaded
' SAVED ': 3,//the module info has been SAVED. Module information has been saved
' READY ': 4,//all dependencies and self are READY to compile. The module's dependencies have been downloaded and are awaiting compilation
' Compiling ': 5,//The module is in compiling now. Module is in compilation
' COMPILED ': 6//The module is COMPILED and Module.exports is available. Module has been compiled
}


function Module (URI, status) {
This.uri = URI
This.status = Status | | 0

This.id is set when saving
This.dependencies is set when saving
This.factory is set when saving
This.exports is set when compiling
This.parent is set when compiling
This.require is set when compiling
}


Module.prototype._use = function (IDs, callback) {
Converting to arrays, unifying operations
Util.isstring (IDS) && (ids = [IDs])
Use the path resolution mechanism inside the module system to parse and return the module path
var URIs = Resolve (IDs, This.uri)

This._load (URIs, function () {
Loads preload files introduced in modules before compiling.
Call the preload preload module again before compiling
Because the Seajs.config configuration preload module can be invoked at any time during code execution
Preload (function () {
Compile each module and pass the exports of each module as a parameter to the callback function
var args = Util.map (URIs, function (URI) {
Return URI? Cachedmodules[uri]._compile (): null
})

if (callback) {
Null causes the this pointer in the callback function to be window
Callback.apply (null, args)
}
})
})
}

The main module loads the dependency module (called a child module) and executes the callback function
Module.prototype._load = function (URIs, callback) {
Filter URIs Array
Case one: The module does not exist in the cache and returns its URI
Case two: The module exists in the cache, but its status < status. READY (ie not ready to compile)
var unloadeduris = util.filter (URIs, function (URI) {
Return URI && (!cachedmodules[uri) | |
Cachedmodules[uri].status < status. READY)
})

var length = Unloadeduris.length
If length is 0 to indicate that the dependency is 0 or that the download is complete, then the callback compilation operation is performed
if (length = = 0) {
Callback ()
Return
}

var remain = length

for (var i = 0; i < length; i++) {
Closures, providing context for onfetched functions
(function (URI) {
Creating a Module Object
var module = Cachedmodules[uri] | |
(Cachedmodules[uri] = new Module (URI, STATUS. Fetching))
If the module is already downloaded, perform a onfetched or fetch operation (Request module)
Module.status >= status. Fetched? Onfetched (): Fetch (URI, onfetched)

function onfetched () {
Cachedmodules[uri] is changed into un-correspondence case
module = Cachedmodules[uri]
If the module state is saved and the module's dependencies are identified, download the dependency module
if (module.status >= status. SAVED) {
Get a list of dependent modules from the module information and do the processing of cyclic dependencies
var deps = getpuredependencies (module)
If there are dependencies, continue downloading
if (deps.length) {
Module.prototype._load (Deps, function () {
CB (MODULE)
})
}
Otherwise directly execute CB
else {
CB (MODULE)
}
}
Maybe failed to fetch successfully, such as 404 or Non-module.
In the cases, just call CB function directly.
If the download module is unsuccessful, such as 404 or the module is not specification (code error), the module state may be fetching, or fetched
The callback function is executed directly at this time, and when the module is compiled, the module will only return null
else {
CB ()
}
}

}) (Unloadeduris[i])
}

Function CB (module) {
Change the module state to ready, and when remain is 0 to indicate that the module dependencies have been exhausted, then execute callback
(Module | | {}). status < Status. READY && (module.status = status. READY)
--remain = = 0 && callback ()
}
}


Module.prototype._compile = function () {
var module = This
If the module has already been compiled, return directly to Module.exports
if (Module.status = = status.compiled) {
Return Module.exports
}

Just return NULL When:
1. The module file is 404.
2. The module file is not written with valid module format.
3. Other error cases.
This is to handle some unusual cases and return null directly at this time
if (Module.status < status. SAVED &&!hasmodifiers (module)) {
return null
}
Change the module state to compiling, indicating that the module is compiling
Module.status = status.compiling

Inside the module, it is a method used to obtain interfaces that other modules provide (called sub modules), synchronous operations
function require (ID) {
To parse the path of a module based on ID
var uri = Resolve (ID, Module.uri)
Get the module from the module cache (note that in fact the dependencies of the sub module as the main module are already downloaded)
var child = Cachedmodules[uri]

Just return NULL when the URI is invalid.
If the child is empty, only indicates that the parameter fill error causes the URI to be incorrect, then returns null directly
if (!child) {
return null
}

Avoids circular calls.
If the status of the sub module is status.compiling, return directly to Child.exports to avoid the iterative compilation of modules because of cyclic dependencies
if (Child.status = = status.compiling) {
Return Child.exports
}
Refers to the module that invokes the current module when initializing. Depending on this property, you can get the call Stack when the module is initialized.
Child.parent = Module
Returns the module.exports of the compiled child
Return Child._compile ()
}
Used inside the module to load the module asynchronously and execute the specified callback after the load completes.
Require.async = function (IDs, callback) {
Module._use (IDs, callback)
}
Use the path resolution mechanism inside the module system to parse and return the module path. The function does not load the module, only returns the parsed absolute path.
Require.resolve = function (ID) {
Return resolve (ID, Module.uri)
}
This property allows you to view all modules that have been loaded into the module system.
In some cases, if you need to reload a module, you can get the URI of the module and then delete the information by deleting Require.cache[uri]. This will be retrieved the next time you use it.
Require.cache = Cachedmodules

Require is a method used to obtain interfaces provided by other modules.
Module.require = Require
Exports is an object that is used to provide a module interface externally.
Module.exports = {}
var factory = Module.factory

When factory is a function, the construction method of the module is represented. By executing this method, you can get the interface that the module provides externally.
if (Util.isfunction (Factory)) {
Compilestack.push (module)
Runinmodulecontext (factory, module)
Compilestack.pop ()
}
When factory is an object, string, and other type of non function, the interface that represents the module is the object, the string equivalent.
such as: define ({"foo": "Bar"});
such as: define (' I am a template. My name is {{name}}. ');
else if (factory!== undefined) {
Module.exports = Factory
}

Change module status to compiled, indicating that the module has been compiled
Module.status = status.compiled
Perform module interface modifications via Seajs.modify ()
Execmodifiers (module)
Return Module.exports
}


Module._define = function (ID, deps, factory) {
var argslength = Arguments.length
Match parameters According to the number of parameters passed in

Define (Factory)
The case of a parameter:
id:undefined
deps:undefined (List of dependent modules will be removed according to regular)
Factory:function
if (argslength = = 1) {
Factory = ID
id = undefined
}
Define (ID | | deps, FACTORY)
Two parameters:

else if (argslength = = 2) {
By default: Define (ID, factory)
ID: ' ... '
deps:undefined
Factory:function
Factory = Deps
Deps = undefined

Define (Deps, Factory)
If the first argument is an array: define (Deps, Factory)
id:undefined
Deps: [...]
Factory:function
if (Util.isarray (ID)) {
Deps = ID
id = undefined
}
}

Parses dependencies.
If Deps is not an array (that is, deps not specified), then a regular expression resolves the dependency
if (!util.isarray (deps) && util.isfunction (Factory)) {
Deps = Util.parsedependencies (factory.tostring ())
}

Meta information and then passes the information to the corresponding module object
var meta = {id:id, dependencies:deps, factory:factory}
var Deriveduri

Try to derive URIs in ie6-9 for anonymous modules.
For Ie6-9, try to get the URI of the module through the interactive script
if (document.attachevent) {
Try to get the current script.
Gets the current script
var script = Util.getcurrentscript ()
if (script) {
Unparesemap the current script's URL to match the key in the module cache
Deriveduri = Util.unparsemap (util.getscriptabsolutesrc (script))
}

if (!deriveduri) {
Util.log (' Failed to derive URI from interactive script for: ',
Factory.tostring (), ' warn ')

Note:if the id-deriving methods above is failed, then falls
To use the OnLoad event to get the URI.
}
}

Gets URI directly for specific module.
If given an ID, then the path is resolved based on the ID
Obviously if you do not specify an ID:
For non-IE browsers, returns undefined (Deriveduri is empty)
For IE browser, return currentscript src
If you specify an ID:
Returns a path URL with Seajs resolution (resolve)
var Resolveduri = ID? Resolve (ID): Deriveduri
Where the URI exists, the module information is stored
if (Resolveduri) {
For IE:
If The package is not the Cachedmodules[deriveduri]
Self, it should assign to the correct module when found.
if (Resolveduri = = Deriveduri) {
var refmodule = Cachedmodules[deriveduri]
if (refmodule && Refmodule.realuri &&
Refmodule.status = = status. SAVED) {
Cachedmodules[deriveduri] = null
}
}
Enclosure Information
var module = Save (Resolveduri, Meta)

For IE:
Assigns the package to Cachedmodules[derivedurl]
if (Deriveduri) {
Cachedmodules[deriveduri] May is undefined in the combo case.
if ((Cachedmodules[deriveduri) | | {}). Status = = status. Fetching) {
Cachedmodules[deriveduri] = module
Module.realuri = Deriveduri
}
}
else {
Store the first module in the Firstmoduleinpackage
Firstmoduleinpackage | | (Firstmoduleinpackage = module)
}
}
A case where the URI does not exist, the module information is stored in the onload callback, where there is a closure
else {
Saves information for "memoizing" work in the OnLoad event.
Because the URI is not known at this time, the meta information is temporarily stored in Anonymousmodulemeta and the module save operation is performed in the onload callback
Anonymousmodulemeta = Meta
}

}

Get the module being compiled
Module._getcompilingmodule = function () {
return Compilestack[compilestack.length-1]
}

Quickly view and get the loaded module interface from the Seajs.cache, the return value is a module.exports array
Selector supports strings and regular expressions
Module._find = function (selector) {
var matches = []

Util.foreach (Util.keys (Cachedmodules), function (URI) {
if (util.isstring (selector) && Uri.indexof (selector) >-1 | |
Util.isregexp (selector) && selector.test (URI)) {
var module = Cachedmodules[uri]
Module.exports && Matches.push (module.exports)
}
})

return matches
}

Modifying module interfaces
Module._modify = function (ID, modifier) {
var uri = Resolve (ID)
var module = Cachedmodules[uri]
If the module exists and is in a compiled state, then the modify interface operation is performed
if (module && module.status = = status.compiled) {
Runinmodulecontext (modifier, module)
}
Otherwise put in the Modify interface cache
else {
Cachedmodifiers[uri] | | (Cachedmodifiers[uri] = [])
Cachedmodifiers[uri].push (modifier)
}

Return SEAJS
}


For plugin Developers
Module.status = STATUS
Module._resolve = Util.id2uri
Module._fetch = Util.fetch
Module.cache = Cachedmodules


Helpers
// -------
List of modules being downloaded
var fetchinglist = {}
List of downloaded modules
var fetchedlist = {}
List of callback functions
var callbacklist = {}
Anonymous module meta information
var Anonymousmodulemeta = null
var firstmoduleinpackage = null
Cyclic dependency Stacks
var circularcheckstack = []

The path of the batch parsing module
function Resolve (IDs, Refuri) {
if (util.isstring (IDs)) {
Return Module._resolve (IDs, Refuri)
}

Return Util.map (IDs, function (ID) {
Return resolve (ID, Refuri)
})
}

function fetch (URI, callback) {
When you fetch, first convert the URI to the map rule
var requesturi = Util.parsemap (URI)
Find in Fethedlist (List of downloaded modules), if any, return directly and execute the callback function
TODO: Why is this step, fetchedlist may exist in the model?
if (Fetchedlist[requesturi]) {
Test/issues/debug-using-map
Cachedmodules[uri] = Cachedmodules[requesturi]
Callback ()
Return
}
In Fetchinglist (which is in the list of downloaded modules), just add the callback function to the list and return directly to the
if (Fetchinglist[requesturi]) {
Callbacklist[requesturi].push (callback)
Return
}
If this is the first time the module has been requested,
The information that is inserted into the module in fetchinglist indicates that the module is already in the download list and initializes the corresponding list of callback functions for the module.
Fetchinglist[requesturi] = True
Callbacklist[requesturi] = [callback]

Fetches it
Get the module, that is, initiate the request
Module._fetch (
RequestUri,

function () {
Inserts the module's information in Fetchedlist, indicating that the module has been downloaded and completed
Fetchedlist[requesturi] = True

Updates module Status
var module = Cachedmodules[uri]
At this point the status may be status.saved, previously said in _define
if (Module.status = = status. Fetching) {
Module.status = status. Fetched
}

Saves anonymous module meta data
Because it is an anonymous module (at this point, the URI is acquired via the closure, where the module information is stored)
and set the Anonymousmodulemeta to null
if (Anonymousmodulemeta) {
Save (URI, Anonymousmodulemeta)
Anonymousmodulemeta = null
}

Assigns the package to Cachedmodules[uri]
See:test/issues/un-correspondence
if (firstmoduleinpackage && module.status = = status. fetched) {
Cachedmodules[uri] = Firstmoduleinpackage
Firstmoduleinpackage.realuri = URI
}
Firstmoduleinpackage = null

Clears
Clears the module information in Fetchinglist because the module is fetched and save
if (Fetchinglist[requesturi]) {
Delete Fetchinglist[requesturi]
}

Calls callbacklist
Call the callback function in turn and clear the list of callback functions
if (Callbacklist[requesturi]) {
Util.foreach (Callbacklist[requesturi], function (FN) {
FN ()
})
Delete Callbacklist[requesturi]
}

},

Config.charset
)
}

function Save (URI, meta) {
var module = Cachedmodules[uri] | | (Cachedmodules[uri] = new Module (URI))

Don ' t override already saved module
The status may have two states at this time:
STATUS. Fetching, call inside define (ID specified), Enclosure information
STATUS. Fetched, called in the onload callback function, enclosure information
if (Module.status < status. SAVED) {
Lets anonymous module ID equal to its URI
Anonymous module (that is, no ID specified), with its URI as ID
module.id = Meta.id | | Uri
An absolute path that resolves a dependency (array) to a module information
Module.dependencies = Resolve (
Util.filter (Meta.dependencies | | [], function (DEP) {
Return!! Dep
}), Uri)
Store factory (module code to be executed, or object or string, etc.)
Module.factory = Meta.factory

Updates module Status
Update module status is saved (note that at this point it only has dependencies, not all downloaded (ie, not yet ready))
Module.status = status. SAVED
}

Return module
}

Execute module code based on module context
Function Runinmodulecontext (FN, module) {
Pass in two parameters related to the module and the module itself
Exports used to expose the interface.
Require used to get dependent modules (Sync) (compilation)
var ret = fn (Module.require, module.exports, module)
Support return value exposure interface form, such as:
return {
Fn1:xx
, fn2:xx
// ...
// }
if (ret!== undefined) {
Module.exports = RET
}
}
Determine if the module has interface modifications
function Hasmodifiers (module) {
Return!! Cachedmodifiers[module.realuri | | module.uri]
}
Modifying module interfaces
function Execmodifiers (module) {
var uri = Module.realuri | | Module.uri
var modifiers = Cachedmodifiers[uri]
Internal variable cachedmodifiers is used to store the modification points defined by the user through the Seajs.modify method
To see if the URI has been changed by modify
if (modifiers) {
Uniformly executes factory on the modification point, returning the modified Module.exports
Util.foreach (modifiers, function (modifier) {
Runinmodulecontext (modifier, module)
})
Deletes the modification point of the Modify method definition to avoid execution again
Delete Cachedmodifiers[uri]
}
}

Get a pure dependency and get an array of dependencies without cyclic dependencies
function Getpuredependencies (module) {
var uri = Module.uri
Filter each dependency to eliminate the possibility of a cyclic dependency and print out a warning log
Return Util.filter (module.dependencies, function (DEP) {
First, the URI of the checked module is placed on the circular dependency check stack, and subsequent checks are used
Circularcheckstack = [URI]
Next, check that the module URI has a circular dependency with its dependent module
var iscircular = iscircularwaiting (CACHEDMODULES[DEP])
if (iscircular) {
If looping, the URI is placed in the circular dependency check stack
Circularcheckstack.push (URI)
Print out circular warning log
Printcircularlog (Circularcheckstack)
}

Return!iscircular
})
}

function iscircularwaiting (module) {
If the dependent module does not exist, then return false because the dependency of the dependent module is not available at this time, so there is no way to judge
Or if the state value of the module equals saved, return false, because the information that represents the module when the module state is saved is already available.
So although the form of cyclic dependencies, but require the main module, the same can be compiled, return to the main module interface (as if Nodejs will return to undefined)
if (!module | | module.status!== status. SAVED) {
return False
}
If this is not the case, then the URI of the dependent module is placed on the cyclic dependency check stack, and subsequent checks are used
Circularcheckstack.push (Module.uri)
To take the dependent module of the dependent module again
var deps = module.dependencies

if (deps.length) {
Check for cyclic dependencies by looping through the check stack (here is the first layer of dependency module checking, and the dependency on the main module loop)
if (Isoverlap (Deps, circularcheckstack)) {
return True
}
If this is not the case, look further and rely on module dependencies to see if they have a circular dependency on the module of the URI in the cyclic dependency check stack
In this case, recursive, cyclic dependent check stack is like forming a chain, the current module in turn to the main module, the main module of the main module ... Until the top of the main module, in turn to determine whether there is dependency
for (var i = 0; i < deps.length; i++) {
if (iscircularwaiting (Cachedmodules[deps[i])) {
return True
}
}
}
If there is no cyclic dependency, the pop out of the module URI that has been pushed in before, and returns false
Circularcheckstack.pop ()
return False
}
Print out circular warning log
function Printcircularlog (stack, type) {
Util.log (' Found circular dependencies: ', Stack.join ('--> '), type)
}
To determine whether two arrays have duplicate values
function Isoverlap (ARRA, ARRB) {
var ArrC = Arra.concat (ARRB)
return arrc.length > Util.unique (arrC). length
}
Read from the configuration file if there are modules that need to be loaded ahead of time
If there is a preload module, first set the preload module to empty (guaranteed to not have to repeat the next load) and load the preload module and execute the callback, if there is no sequential execution
function preload (callback) {
var preloadmods = Config.preload.slice ()
Config.preload = []
Preloadmods.length? Globalmodule._use (Preloadmods, Callback): Callback ()
}


Public API
Externally exposed APIs
// ----------
Global module, can be considered as a page module, the page js,css files are loaded through it
Module initial state is Compiled,uri is the URI of the page
var globalmodule = new Module (Util.pageuri, status.compiled)

Page js,css File Loader
Seajs.use = function (IDs, callback) {
Loads preload modules before all other modules.
Pre-loading modules
Preload (function () {
Globalmodule._use (IDs, callback)
})

Chain
Return SEAJS
}


For normal users
For normal user calls
Seajs.define = Module._define
Seajs.cache = Module.cache
Seajs.find = Module._find
Seajs.modify = module._modify


For plugin Developers
For developers to use
SEAJS.PLUGINSDK = {
Module:module,
Util:util,
Config:config
}

}) (Seajs, Seajs._util, Seajs._config)

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.