Preface
SeaJS is very powerful. SeaJS can load any JavaScript module and css module style. SeaJS will ensure that when you use a module, you have loaded other modules you depend on into the script running environment.
By referenceAboveFor demo, we combine source code analysis behind simple API calls, what techniques are used to implement dependency loading of each module and export module APIs.
Module class and state class
First, a Module class is defined, corresponding to a module
function Module(uri, deps) { = uri = deps || [] = null = 0 // Who depends on me this._waitings = {} // The number of unloaded dependencies this._remain = 0 }
Module has some properties, uri corresponds to the absolute url of the module,There will be an introduction in the function; dependencies are arrays of dependent modules; exports are the exported API; status is the current status code; _waitings object is the hash table of other modules currently dependent on the module, where the key is the url of other modules; _remain is a counter, recording the number of modules that have not been loaded.
var STATUS = = { // 1 - The `` is being fetched FETCHING: 1, // 2 - The meta data has been saved to cachedMods SAVED: 2, // 3 - The `` are being loaded LOADING: 3, // 4 - The module are ready to execute LOADED: 4, // 5 - The module is being executed EXECUTING: 5, // 6 - The `` is available EXECUTED: 6 }
The above is a state object, which records the current state of the module: the module initialization state is 0, when the module is loaded, it is a state fetching; the module is loaded and cached in cacheMods, it is a state saved; the loading state means that other dependent modules are loading the module; loaded means that all dependent modules are loaded, execute the callback function of the module, and set whether other modules that depend on the module are still not loaded, and execute the callback function if the loading is completed; the executing state means that the module is being executed; executed means that the execution is completed, and you can use the exports API.
Module definition
CommonJS specifications stipulatedefine
Function to define a module. define can accept 1, 2, 3 parameters, but for the Module/wrappings specification,or
define
Functions can only accept one parameter, namely factory functions or objects. However, in principle, there is no essential difference in the number of parameters accepted, but the library adds additional module names to the background.
Seajs encourages usedefine(function(require,exports,module){})
This way of defining modules is a typical implementation of the Module/wrappings specification. But in the background, the factory function is parsedrequire
Method to get the dependent module and set the id and url to the module.
// Define a module = function (id, deps, factory) { var argsLen = // define(factory) if (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 dependencies according to the module factory code // If deps is non-array, the serialization factory function gets the incoming parameter. if (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(()) } var meta = { id: id, uri: (id), // Absolute url deps: deps, factory: factory } // Try to derive uri in IE6-9 for anonymous modules // Export the uri of anonymous module if (! && ) { var script = getCurrentScript() if (script) { = } // NOTE: If the id-deriving methods above is failed, then falls back // to use onload event to get the uri } // Emit `define` event, used in nocache plugin, seajs node version etc emit("define", meta) ? (, meta) : // Save information for "saving" work in the script onload event anonymousMeta = meta }
The last module definition is passedMethod, save the module to cachedMods cache body.
parseDependencies
The method cleverly obtains dependency modules. It uses the function's string representation and uses regular to obtainrequire(“…”)
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 we use function serialization (incoming factory) for string matching, looking for the keyword required ("...") (SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { (m2) } }) return ret }
Asynchronous loading module
There are many ways to load modules. The xhr method can be loaded synchronously or asynchronously, but there are homologous problems, so it is difficult to use here. in additionscript tag
The method can ensure parallel loading and sequential execution in IE and modern browsers.script element
The method can also guarantee parallel loading but not sequential execution, so both methods can be used.
In seajs, it is adoptedscript element
This method is used to load js/css resources in parallel, and hack it for old versions of webkit browsers.
function request(url, callback, charset) { var isCSS = IS_CSS_RE.test(url) var node = (isCSS ? "link" : "script") if (charset) { var cs = isFunction(charset) ? charset(url) : charset if (cs) { = cs } } // Add the onload function. addOnload(node, callback, isCSS, url) if (isCSS) { = "stylesheet" = url } else { = true = url } // For some cache cases in IE 6-8, the script executes IMMEDIATELY after // the end of the insert execution, so use `currentlyAddingScript` to // hold current node, for deriving url in `define` call currentlyAddingScript = node // ref: #185 & /ticket/2709 baseElement ? (node, baseElement) : (node) currentlyAddingScript = null } function addOnload(node, callback, isCSS, url) { var supportOnload = "onload" in node // for Old WebKit and Old Firefox if (isCSS && (isOldWebKit || !supportOnload)) { setTimeout(function() { pollCss(node, callback) }, 1) // Begin after node insertion return } if (supportOnload) { = onload = function() { emit("error", { uri: url, node: node }) onload() } } else { = function() { if (/loaded|complete/.test()) { onload() } } } function onload() { // Ensure only run once and handle memory leak in IE = = = null // Remove the script to reduce memory leak if (!isCSS && !) { (node) } // Dereference the node node = null callback() } } // Methods to determine the loading of old webkits and CSS nodes that do not support onloadfunction pollCss(node, callback) { var sheet = var isLoaded // for WebKit < 536 if (isOldWebKit) { if (sheet) { isLoaded = true } } // for Firefox < 9.0 else if (sheet) { try { if () { isLoaded = true } } catch (ex) { // The value of `` is changed from "NS_ERROR_DOM_SECURITY_ERR" // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0 // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR" if ( === "NS_ERROR_DOM_SECURITY_ERR") { isLoaded = true } } } setTimeout(function() { if (isLoaded) { // Place callback here to give time for style rendering callback() } else { pollCss(node, callback) } }, 20) }
Some of the details need to be paid attention to when usingscript element
When inserting a script node, try to insert it into the head as the first child node, because this is a difficult bug to find:
GLOBALEVAL WORKS INCORRECTLY IN IE6 IF THE CURRENT PAGE HAS <BASE HREF> TAG IN THE HEAD
fetch module
When initializing the Module object, the status is 0, and the corresponding js file of the object is not loaded. To load the js file, you need to use the mentioned above.request
Method, but it is also impossible to just load the file. You also need to set the state of the module object and load other modules that the module depends on.
These logics arefetch
The method is reflected:
// Fetch a module // Load the module, the function is called in the fetch function = function(requestCache) { var mod = this var uri = = // Emit `fetch` event for plugins such as combo plugin var emitData = { uri: uri } emit("fetch", emitData) var requestUri = || uri // Empty uri or a non-CMD module if (!requestUri || fetchedList[requestUri]) { () return } if (fetchingList[requestUri]) { callbackList[requestUri].push(mod) return } fetchingList[requestUri] = true callbackList[requestUri] = [mod] // Emit `request` event for plugins such as text plugin emit("request", emitData = { uri: uri, requestUri: requestUri, onRequest: onRequest, charset: }) if (!) { requestCache ? requestCache[] = sendRequest : sendRequest() } function sendRequest() { (, , ) } // Callback function function onRequest() { delete fetchingList[requestUri] fetchedList[requestUri] = true // Save meta data of anonymous module if (anonymousMeta) { (uri, anonymousMeta) anonymousMeta = null } // Call callbacks var m, mods = callbackList[requestUri] delete callbackList[requestUri] while ((m = ())) () } }
inThat's from the previous section
request
method.onRequest
As a callback function, the function is to load other dependent modules of the module.
Summarize
The above is the entire content of the seajs module's dependency loading and module API export. The editor will introduce the loading of dependencies between modules and module execution in the next section. Interested friends can continue to follow me.