Objective
Seajs is very powerful, SEAJS can load arbitrary JavaScript modules and CSS module styles, SEAJS will ensure that when you use a module, you have already relied on other modules to load into the script run environment.
By reference to the above demo, we combine the source analysis in the simple API call behind, what is the use of techniques to implement the various modules of the dependent loading and the module API export.
module classes and Status classes
First, a module class is defined, corresponding to a modular
function Module (URI, deps) {
This.uri = uri
this.dependencies = Deps | | []
this.exports = null
this.status = 0
//Who depends on me
this._waitings = {}
//The number of UNL oaded dependencies
this._remain = 0
}
module has some attributes, the URI pair should be the absolute URL of the module, in the Module.define function will be introduced; dependencies is an array of dependent modules; Exports for the exported api;status is the current status code; _ The Waitings object is a hash table of other modules that are currently dependent on the module, where key is the Url;_remain for the other modules and the number of modules that have not been loaded.
var STATUS = Module.status = {
//1-the ' Module.uri ' is being fetched fetching:1
,
//2-the meta data has been saved to Cachedmods
saved:2,
//3-the ' module.dependencies ' are being, loaded Loading:3
,
//4-th E module are ready to execute
loaded:4,
//5-the module is being executed
,
//Executing:5 ' m Odule.exports ' is available
executed:6
}
The above is the state object, the current state of the recording module: The module initialization state is 0, when the module is loaded, the state is fetching, the module is loaded and cached after Cachemods, the state saved;loading state means that other dependent modules of the module are being loaded Loaded indicates that all dependent modules have been loaded, executed the callback function of the module, and set whether other modules that depend on the module still have dependent modules not loaded, execute the callback function if the load is completed, and executing state that the module is executing; executed is executed. You can use the exports API.
The definition of a module
The COMMONJS specification define defines a module by a function. Define can accept 1,2,3 parameters, but for the module/wrappings specification, module.declare define a function can only accept one parameter, that is, a factory function or an object. In principle, however, there is no intrinsic difference between the number of parameters accepted, except that the library adds additional module names to the background.
Seajs encourages the use of define (function (Require,exports,module) {}) the way this module is defined, which is a typical implementation of the Module/wrappings specification. However, in the background, the require method of the factory function is parsed to obtain the dependent module and set the ID and URL to the module.
Define a module module.define = function (ID, deps, factory) {var Argslen = arguments.length//Define (Factory) I
F (Argslen = = 1) {factory = id id = undefined} else if (Argslen = = 2) {factory = Deps//define (Deps, Factory) if (IsArray (ID)) {deps = id id = undefined}//define (ID, factory) else {deps = undefined}}//Parse Depe
Ndencies according to the Module factory code//If Deps is not an array, then the sequence factory function gets the incoming parameter. if (!isarray (deps) && isfunction (Factory)) {deps = Parsedependencies (Factory.tostring ())} var meta = {ID: ID, uri:Module.resolve (ID),//absolute URL deps:deps, factory:factory}//Try to derive URI into ie6-9 for anonymous mod Ules//Export the URI of the anonymous module if (!meta.uri && doc.attachevent) {var script = Getcurrentscript () if (script) {Meta.ur i = script.src}//Note:if The id-deriving methods above is failed, then falls back//To use the onload and get th E uri}//Emit ' Define ' event, used in NoCache plugin, SEAJS nodeVersion etc emit ("define", Meta) Meta.uri? Module.save (Meta.uri, Meta)://Save information for "saving" work in the script onload event Anonymousmeta = meta}
At the end of the module definition, the Module.save module is saved to the Cachedmods cache body by means of a method.
parseDependenciesThe method is more ingenious to obtain the dependent module. He uses the string representation of the function to get require(“…”) the module name in.
var require_re =/"(?: \ \"| [^"]) *"|' (?:\ \'| [^']) * ' |\/\*[\s\s]*?\*\/|\/(?: \ \\/| [^\/\r\n]) +\/(? =[^\/]) |\/\/.*|\.\s*require| (?:^| [^$]) \brequire\s*\ (\s* (["']) (. +?) \1\s*\)/g
var slash_re =/\\\\/g
function parsedependencies (code) {
var ret = []
// Here you use function serialization (the Incoming factory) for string matching, looking for REQUIRE ("...") keyword
code.replace (slash_re, "")
. Replace (Require_re, Function (M, m1, m2) {
if (m2) {
ret.push (m2)
}
}) return
ret
}
Load Modules asynchronously
Loading modules can be loaded in a variety of ways, either synchronously or asynchronously, but there is a xhr problem, which is difficult to use here. In addition, script tag in IE and modern browsers, parallel loading and sequential execution can be guaranteed, and the method can be script element used in parallel loading without guarantee of sequential execution.
In Seajs, the script element method is used to load js/css resources in parallel, and it is hack to load CSS for older versions of WebKit browsers.
function request (URL, callback, CharSet) {var iscss = is_css_re.test (URL) var node = doc.createelement (iscss?)
' Link ': ' Script ' if (charset) {var cs = isfunction (charset)? CharSet (URL): CharSet if (cs) {Node.charset = CS
}//Add the onload function. Addonload (node, callback, ISCSS, URL) if (iscss) {Node.rel = "stylesheet" node.href = URL} else {node.async = tr UE node.src = URL}//For some cache cases in IE 6-8, "script executes IMMEDIATELY after//" The end of the insert execution, so with ' currentlyaddingscript ' to//hold current node, for deriving URLs in ' define ' call CURRENTLYADDINGSCR
IPT = node//ref: #185 & http://dev.jquery.com/ticket/2709 baseelement? Head.insertbefore (node, baseelement): Head.appendchild (node) currentlyaddingscript = null} function addonload (node, Callback, ISCSS, url) {var supportonload = "onload" in node//as Old WebKit and old Firefox if (iscss && (i Soldwebkit | | !supportonload)) {settimeout (fUnction () {POLLCSS (node, callback)}, 1)//Begin after node insertion return} if (supportonload) {node.onload = OnLoad node.onerror = function () {Emit ("error", {uri:url, node:node}) onload ()}} else {Node.onreadystatech Ange = function () {if (/loaded|complete/.test (node.readystate)) {onload ()}}} function onload () {//ensure on Ly run once and handle memory leak in IE node.onload = Node.onerror = Node.onreadystatechange = NULL//Remove the SCRI PT to reduce memory leak if (!iscss &&!data.debug) {Head.removechild (node)}//Dereference the node node
= NULL callback ()}}///For the old WebKit and not the onload CSS node to determine the loaded method function pollcss (node, callback) {var sheet = Node.sheet
var isLoaded//For WebKit < 536 if (Isoldwebkit) {if (sheet) {isLoaded = true}}//For Firefox < 9.0 else if (sheet) {try {if (sheet.cssrules) {isLoaded = true} \ catch (ex) {//The value of ' ex.name ' is changed From "Ns_error_dom_secuRity_err "//To" Securityerror "since Firefox 13.0. But Firefox is less than 9.0/in-here and so it's OK to just rely on "ns_error_dom_security_err" if (ex.name = = "Ns_err" Or_dom_security_err ") {isLoaded = True}}} settimeout (function () {if (isLoaded) {//Place callback-to-gi ve time for style rendering callback ()} else {POLLCSS (node, callback)}}, 20)}
Some of the details also need to be noted when inserting the script element script node in a method, as far as the first child node is inserted into the head, due to a bug that is difficult to discover:
GLOBALEVAL WORKS INCORRECTLY IN IE6 IF THE CURRENT PAGE HAS <BASE HREF> TAG IN THE HEAD
Fetch module
When initializing a module object, the state is 0, the corresponding JS file is not loaded, to load the JS file, you need to use the method mentioned in the previous section request , but it is not possible to simply load the file, you also need to set the state of the module object and other modules that load the module dependencies.
The logic is fetch embodied in the method:
Fetch a module//load the modules, call the Seajs.request function in the fetch function Module.prototype.fetch = function (requestcache) {var mod = this var uri = Mod.uri Mod.status = status. Fetching//Emit ' fetch ' event for plugins such as combo plugin var emitdata = {Uri:uri} Emit ("Fetch", Emitdata) v Ar requesturi = Emitdata.requesturi | | URI//Empty URI or a non-cmd module if (!requesturi | | fetchedlist[requesturi]) {mod.load () return} if (Fetchin Glist[requesturi]) {Callbacklist[requesturi].push (mod) return} Fetchinglist[requesturi] = True Callbacklist[reques Turi] = [mod]//Emit ' request ' event for plugins such as text plugin Emit ("request", Emitdata = {Uri:uri, requestu
Ri:requesturi, Onrequest:onrequest, Charset:data.charset}) if (!emitdata.requested) {Requestcache? Requestcache[emitdata.requesturi] = sendrequest:sendrequest ()} function SendRequest () {seajs.request (emitData.req Uesturi, Emitdata.onrequest, Emitdata.charset)}//Callback functions function OnrequesT () {delete Fetchinglist[requesturi] fetchedlist[requesturi] = TRUE//Save meta data of anonymous module if (anonym Ousmeta) {Module.save (URI, anonymousmeta) Anonymousmeta = null}//Call callbacks var m, mods = Callbacklist[reque Sturi] Delete Callbacklist[requesturi] while ((M = Mods.shift ())) M.load ()}}
This seajs.request is the method of the previous section request . onRequestas a callback function, the function is to load other dependent modules of the module.
Summarize
The above is the Seajs module's dependency loading and the module API of the entire content, the next section will introduce the module dependencies between the load and the implementation of the module. Interested friends can continue to focus on the cloud-dwelling community.