博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
seajs源码分析
阅读量:4674 次
发布时间:2019-06-09

本文共 28091 字,大约阅读时间需要 93 分钟。

seajs主要做了2件事

1.定义什么是模块,如何声明模块:id、deps、factory、exports ----define=function(id,deps,factory){return exports}

2.管理模块依赖,解决自动加载。本质其实是个加载器loader:id配上路径规则,factory可以提取依赖模块,递归遍历后自动加载js文件

  核心的代码是util-path.js和module.js

 

发现seajs已经修订到3.0.0部分实现有改动,有些地方随手写的,也没求证过估计误人子弟不少。忙着review下,后面再补了3、0版的module.js注释贻笑大方

对外接口

seajs.use    执行的入口方法

define       模块定义的初始化方法

seajs.use("a")->Module.get("a")                           m.load();执行fetch后加载define调用Module.save跟新deps                           m.onload();                           m.exec();    子模块:             deps->Module.get()             fetch/load 通知回调-> 主模板onload 模块状态:fetch--Module.fetch  拉取模板saved--Module.saved  拉取模板成功后就save模块(define会提取deps)loading--m.load()   检测依赖模块是否全部加载loaded--m.onload()  已加载全部模块后触发executing/executed-m.exec() seajs.use->mod.onload->mod.callback()->mod.exec(); use是seajs模块入口函数定义模块:

Module.use(seajs.use)

Module.define(global.define)

 

核心类:Module

静态方法

Module.use(使用模块load后exec)、Module.preload(获取data.preload参数后执行Module.use)、Module.get(拿到模块,若无则创建)

模块use的执行过程

load     开始加载目标主模块

get      创建主模块

fetch     发送request请求加载模块,加载成功后执行define方法会抽取依赖deps

save      加载成功后会执行define方法,解析依赖的deps。且保持Module.save

load     每次模块加载成功后onRequest回调主模块load判断是否remain==0,调用主模块onload(新版改用Module.prototype.pass)。每次子模板加载成功后,都要通知依赖自己的主模板。更新状态以便判断是否进入onload

onload    全部加载完触发onload,回调callback(seajs.use(id,callback))。只有主模板会有onload执行

exec     执行模块factory(即define(fn)的fn),且将require关键字映射exec上,依次执行所有依赖模块

/*    创建seajs对象,标识版本号。创建data数据对象,data挂在seajs为外部访问*/var seajs = global.seajs = {  // The current version of Sea.js being used  version: "@VERSION"}var data = seajs.data = {}/*    seajs自己的事件触发,on绑定事件,off移除,emit触发*//** * util-events.js - The minimal events support */var events = data.events = {}// Bind eventseajs.on = function(name, callback) {  var list = events[name] || (events[name] = [])  list.push(callback)  return seajs}// Remove event. If `callback` is undefined, remove all callbacks for the// event. If `event` and `callback` are both undefined, remove all callbacks// for all eventsseajs.off = function(name, callback) {  // Remove *all* events  if (!(name || callback)) {    events = data.events = {}    return seajs  }  var list = events[name]  if (list) {    if (callback) {      for (var i = list.length - 1; i >= 0; i--) {        if (list[i] === callback) {          list.splice(i, 1)        }      }    }    else {      delete events[name]    }  }  return seajs}// Emit event, firing all bound callbacks. Callbacks are passed the same// arguments as `emit` is, apart from the event namevar emit = seajs.emit = function(name, data) {  var list = events[name], fn  if (list) {    // Copy callback lists to prevent modification    list = list.slice()    // Execute event callbacks    while ((fn = list.shift())) {      fn(data)    }  }  return seajs}/*    类型判断和计数函数*//** * util-lang.js - The minimal language enhancement */function isType(type) {  return function(obj) {    return Object.prototype.toString.call(obj) === "[object " + type + "]"  }}var isObject = isType("Object")var isString = isType("String")var isArray = Array.isArray || isType("Array")var isFunction = isType("Function")var _cid = 0function cid() {  return _cid++}/*    解析路径的处理方法,包括:    dirname        获取路径的目录    realpath    过滤目录中的./ ../    normalize    补全文件名.js后缀    parseAlias    解析配置别名    parsePaths    解析配置中的path    parseVars    解析配置中{}    parseMap    加载路径根据配置匹配规则映射    addBase        通过id,路由规则生成完整绝对路径        id2Uri        根据id获取uri,完整的绝对路径        loaderDir    seajs文件路径的获取:根据id=seajsnode查找seajs的dom,获取src后匹配目录*//** * util-path.js - The utilities for operating path such as id, uri */var DIRNAME_RE = /[^?#]*\//var DOT_RE = /\/\.\//gvar DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//// Extract the directory portion of a path// dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"// ref: http://jsperf.com/regex-vs-split/2function dirname(path) {  return path.match(DIRNAME_RE)[0]}// Canonicalize a path// realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"function realpath(path) {  // /a/b/./c/./d ==> /a/b/c/d  path = path.replace(DOT_RE, "/")  // a/b/c/../../d  ==>  a/b/../d  ==>  a/d  while (path.match(DOUBLE_DOT_RE)) {    path = path.replace(DOUBLE_DOT_RE, "/")  }  return path}// Normalize an id// normalize("path/to/a") ==> "path/to/a.js"// NOTICE: substring is faster than negative slice and RegExpfunction normalize(path) {  var last = path.length - 1  // If the uri ends with `#`, just return it without '#'  if (path.charAt(last) === "#") {    return path.substring(0, last)  }  return  (path.substring(last - 2) === ".js" ||      path.indexOf("?") > 0 ||      path.substring(last - 3) === ".css") ? path : path + ".js"}var PATHS_RE = /^([^/:]+)(\/.+)$/var VARS_RE = /{([^{]+)}/gfunction parseAlias(id) {  var alias = data.alias  return alias && isString(alias[id]) ? alias[id] : id}function parsePaths(id) {  var paths = data.paths  var m  if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {    id = paths[m[1]] + m[2]  }  return id}function parseVars(id) {  var vars = data.vars  if (vars && id.indexOf("{") > -1) {    id = id.replace(VARS_RE, function(m, key) {      return isString(vars[key]) ? vars[key] : m    })  }  return id}function parseMap(uri) {  var map = data.map  var ret = uri  if (map) {    for (var i = 0, len = map.length; i < len; i++) {      var rule = map[i]      ret = isFunction(rule) ?          (rule(uri) || uri) :          uri.replace(rule[0], rule[1])      // Only apply the first matched rule      if (ret !== uri) break    }  }  return ret}var ABSOLUTE_RE = /^\/\/.|:\//var ROOT_DIR_RE = /^.*?\/\/.*?\//function addBase(id, refUri) {  var ret  var first = id.charAt(0)  // Absolute  if (ABSOLUTE_RE.test(id)) {    ret = id  }  // Relative  else if (first === ".") {    ret = realpath((refUri ? dirname(refUri) : data.cwd) + id)  }  // Root  else if (first === "/") {    var m = data.cwd.match(ROOT_DIR_RE)    ret = m ? m[0] + id.substring(1) : id  }  // Top-level  else {    ret = data.base + id  }  return ret}function id2Uri(id, refUri) {  if (!id) return ""  id = parseAlias(id)  id = parsePaths(id)  id = parseVars(id)  id = normalize(id)  var uri = addBase(id, refUri)  uri = parseMap(uri)  return uri}var doc = documentvar loc = locationvar cwd = dirname(loc.href)var scripts = doc.getElementsByTagName("script")// Recommend to add `seajsnode` id for the `sea.js` script elementvar loaderScript = doc.getElementById("seajsnode") ||    scripts[scripts.length - 1]// When `sea.js` is inline, set loaderDir to current working directoryvar loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd)function getScriptAbsoluteSrc(node) {  return node.hasAttribute ? // non-IE6/7      node.src :    // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx      node.getAttribute("src", 4)}/*    request     动态加载css或者script到head                创建dom原生link、script标签    addOnload    定义浏览器兼容的异步请求回调    getCurrentScript    获取当前资源加载状态为进行中interactive的资源*//** * util-request.js - The utilities for requesting script and style files * ref: tests/research/load-js-css/test.html */var head = doc.getElementsByTagName("head")[0] || doc.documentElementvar baseElement = head.getElementsByTagName("base")[0]var IS_CSS_RE = /\.css(?:\?|$)/ivar READY_STATE_RE = /^(?:loaded|complete|undefined)$/var currentlyAddingScriptvar interactiveScript// `onload` event is supported in WebKit < 535.23 and Firefox < 9.0// ref://  - https://bugs.webkit.org/show_activity.cgi?id=38995//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_eventsvar isOldWebKit = (navigator.userAgent    .replace(/.*AppleWebKit\/(\d+)\..*/, "$1")) * 1 < 536function 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    }  }  addOnload(node, callback, isCSS)  if (isCSS) {    node.rel = "stylesheet"    node.href = url  }  else {    node.async = true    node.src = 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 & http://dev.jquery.com/ticket/2709  baseElement ?      head.insertBefore(node, baseElement) :      head.appendChild(node)  currentlyAddingScript = null}function addOnload(node, callback, isCSS) {  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))  // for Old WebKit and Old Firefox  if (missingOnload) {    setTimeout(function() {      pollCss(node, callback)    }, 1) // Begin after node insertion    return  }  node.onload = node.onerror = node.onreadystatechange = function() {    if (READY_STATE_RE.test(node.readyState)) {      // Ensure only run once and handle memory leak in IE      node.onload = node.onerror = node.onreadystatechange = null      // Remove the script to reduce memory leak      if (!isCSS && !data.debug) {        head.removeChild(node)      }      // Dereference the node      node = null      callback()    }  }}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, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"      if (ex.name === "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)}function getCurrentScript() {  if (currentlyAddingScript) {    return currentlyAddingScript  }  // For IE6-9 browsers, the script onload event may not fire right  // after the the script is evaluated. Kris Zyp found that it  // could query the script nodes and the one that is in "interactive"  // mode indicates the current script  // ref: http://goo.gl/JHfFW  if (interactiveScript && interactiveScript.readyState === "interactive") {    return interactiveScript  }  var scripts = head.getElementsByTagName("script")  for (var i = scripts.length - 1; i >= 0; i--) {    var script = scripts[i]    if (script.readyState === "interactive") {      interactiveScript = script      return interactiveScript    }  }}/** * module.js - The core of module loader *///系统全局对象,用来保存全局数据和状态var cachedMods = seajs.cache = {}var anonymousMetavar fetchingList = {}var fetchedList = {}var callbackList = {}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 - The module are ready to execute  LOADED: 4,  // 5 - The module is being executed  EXECUTING: 5,  // 6 - The `module.exports` is available  EXECUTED: 6}//模块类   id、deps、exports 构造函数function Module(uri, deps) {  this.uri = uri  this.dependencies = deps || []  this.exports = null  this.status = 0//模块状态  // The number of unloaded dependencies  this._remain = 0  // Who depend on me  this._waitings = {}}//Module静态方法,Module.use使用模块,Module.preload调用data.preload后回调Module.use// Load preload modules before all other modulesModule.preload = function(callback) {  var preloadMods = data.preload  var len = preloadMods.length  if (len) {    Module.use(preloadMods, function() {      // Remove the loaded preload modules      preloadMods.splice(0, len)      // Allow preload modules to add new preload modules      Module.preload(callback)    }, data.cwd + "_preload_" + cid())  }  else {    callback()  }}//入口模块,ids--依赖模块,uri模块id:获取uri模块// Use function is equal to load a anonymous moduleModule.use = function (ids, callback, uri) {  var mod = Module.get(uri, isArray(ids) ? ids : [ids])  mod.callback = function() {    var exports = []    var uris = mod.resolve()    for (var i = 0, len = uris.length; i < len; i++) {      exports[i] = cachedMods[uris[i]].exec()    }    if (callback) {      callback.apply(global, exports)    }    delete mod.callback  }  mod.load()}//获取uri模块,若cachedMods中无则创建个新模块Module.get = function(uri, deps) {  return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))}// seajs.use 入口方法// 执行过程:A依赖B,B依赖C,seajs.use(A):/*fetch            获取A模块的依赖模块(B),B拉取成功后回调A.load(每次依赖的文件onRquest,要跟新自己的状态)load            遍历A模块依赖的所有模块状态(B/C),直到remain==0,触发onloadonload            当A模块所有依赖都已加载,触发A.callbackexec            seajs.use 将执行模块的callback设置为exec,调用A.exec,且A模块中的require映射了exec方法,链式的执行下去。。*/seajs.use = function(ids, callback) {  Module.preload(function() {    Module.use(ids, callback, data.cwd + "_use_" + cid())  })  return seajs}//将依赖的模块通过fetch方法注册,最终执行load请求js,onRequest回调onload// Load module.dependencies and fire onload when all doneModule.prototype.load = function() {  var mod = this  // If the module is being loaded, just wait it onload call  if (mod.status >= STATUS.LOADING) {    return  }  mod.status = STATUS.LOADING  // Emit `load` event for plugins such as combo plugin  var uris = mod.resolve()//获取依赖uris  emit("load", uris)  var len = mod._remain = uris.length  var m  // Initialize modules and register waitings 拿到依赖模块注册依赖  for (var i = 0; i < len; i++) {    m = Module.get(uris[i])//获取依赖module    if (m.status < STATUS.LOADED) {
//模块未加载 // Maybe duplicate m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1//记录下有几个模块依赖自己 } else { mod._remain--//加载后-- } } //若所有依赖都加载后,执行onload if (mod._remain === 0) { mod.onload() return } // Begin parallel loading var requestCache = {} for (i = 0; i < len; i++) { m = cachedMods[uris[i]] if (m.status < STATUS.FETCHING) { m.fetch(requestCache) } else if (m.status === STATUS.SAVED) { m.load() } } // Send all requests at last to avoid cache bug in IE6-9. Issues#808 for (var requestUri in requestCache) { if (requestCache.hasOwnProperty(requestUri)) { requestCache[requestUri]() } }}//将模块的所有依赖的uri都注册到此模块的requestCache列表中// Fetch a moduleModule.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) var requestUri = emitData.requestUri || uri // Empty uri or a non-CMD module if (!requestUri || fetchedList[requestUri]) { mod.load() 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: data.charset }) if (!emitData.requested) { requestCache ? requestCache[emitData.requestUri] = sendRequest : sendRequest() } function sendRequest() { request(emitData.requestUri, emitData.onRequest, emitData.charset) } function onRequest() { delete fetchingList[requestUri] fetchedList[requestUri] = true // Save meta data of anonymous module if (anonymousMeta) { save(uri, anonymousMeta) anonymousMeta = null } // Call callbacks var m, mods = callbackList[requestUri] delete callbackList[requestUri] while ((m = mods.shift())) m.load() }}//若模块的依赖加载全部加载触发onload回调mod.callback,同时通知所有依赖他的模块// Call this method when module is loadedModule.prototype.onload = function() { var mod = this mod.status = STATUS.LOADED if (mod.callback) { mod.callback() } // Notify waiting modules to fire onload var waitings = mod._waitings var uri, m for (uri in waitings) { if (waitings.hasOwnProperty(uri)) { m = cachedMods[uri] m._remain -= waitings[uri] if (m._remain === 0) { m.onload() } } } // Reduce memory taken delete mod._waitings delete mod._remain}//解析模块依赖的uris 获取绝对uris// Resolve module.dependenciesModule.prototype.resolve = function() { var mod = this var ids = mod.dependencies var uris = [] for (var i = 0, len = ids.length; i < len; i++) { uris[i] = resolve(ids[i], mod.uri) } return uris}//触发resolve事件,返回id2Uri// Helpersfunction resolve(id, refUri) { // Emit `resolve` event for plugins such as text plugin var emitData = { id: id, refUri: refUri } emit("resolve", emitData) return emitData.uri || id2Uri(emitData.id, refUri)}//加载完所有依赖的模块后,执行自身模块,同时通过关键字require将依赖的模块顺序执行// Execute a moduleModule.prototype.exec = function () { var mod = this // When module is executed, DO NOT execute it again. When module // is being executed, just return `module.exports` too, for avoiding // circularly calling if (mod.status >= STATUS.EXECUTING) { return mod.exports//模块已执行 } mod.status = STATUS.EXECUTING // Create require var uri = mod.uri //执行require的id function require(id) { return cachedMods[require.resolve(id)].exec() } //获得id模块的绝对路径 require.resolve = function(id) { return resolve(id, uri) } //异步请求uri require.async = function(ids, callback) { Module.use(ids, callback, uri + "_async_" + cid()) return require } // Exec factory var factory = mod.factory var exports = isFunction(factory) ? factory(require, mod.exports = {}, mod) : factory if (exports === undefined) { exports = mod.exports } // Emit `error` event if (exports === null && !IS_CSS_RE.test(uri)) { emit("error", mod) } // Reduce memory leak delete mod.factory mod.exports = exports mod.status = STATUS.EXECUTED // Emit `exec` event emit("exec", mod) return exports}//对外的接口 define(function(){...})用于定义模块,初始化模块的基础属性(并不执行模块factory方法)Module.define.cmd = {}global.define = Module.define// Define a moduleModule.define = function (id, deps, factory) { var argsLen = arguments.length // 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 (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(factory.toString()) } var meta = { id: id, uri: resolve(id), deps: deps, factory: factory } // Try to derive uri in IE6-9 for anonymous modules if (!meta.uri && doc.attachEvent) { var script = getCurrentScript() if (script) { meta.uri = script.src } // 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.uri ? save(meta.uri, meta) : // Save information for "saving" work in the script onload event anonymousMeta = meta}function save(uri, meta) { var mod = Module.get(uri) // Do NOT override already saved modules if (mod.status < STATUS.SAVED) { mod.id = meta.id || uri mod.dependencies = meta.deps || [] mod.factory = meta.factory mod.status = STATUS.SAVED }}// For Developers 调试接口seajs.Module = Moduledata.fetchedList = fetchedListdata.cid = cid//获取模块idseajs.resolve = id2Uri//获取模块exports接口seajs.require = function(id) { return (cachedMods[resolve(id)] || {}).exports}

 

 

 附commonjs、seajs、requirejs比较

#commonjs 与seajs
* commonjs :node的编写规范,为运行在服务器端的非浏览器环境的模块系统定义。模块间的依赖通过require同步加载,导致浏览器端无法采用这种设计
* seajs:将commonjs的规范扩展后,可以在浏览器环境下运行的模式系统设计规范。通过define(function(){});
详细说明:
 
#requirejs 与seajs
requirejs是amd规范,seajs是cmd规范
RequireJS 和 SeaJS 区别:
 ,执行define的延迟 
 
 
补充Sea.js 3.0.0 module.js注释
function Module(uri, deps) {  this.uri = uri  this.dependencies = deps || []  this.status = 0  this._entry = []}//获取模板的全路径// Resolve id to uriModule.resolve = function(id, refUri) {  // Emit `resolve` event for plugins such as text plugin  var emitData = { id: id, refUri: refUri }  emit("resolve", emitData)  return emitData.uri || seajs.resolve(emitData.id, refUri)}//定义一个模块mod// Define a moduleModule.define = function (id, deps, factory) {  var argsLen = arguments.length  // 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 (!isArray(deps) && isFunction(factory)) {    deps = parseDependencies(factory.toString())  }  var meta = {    id: id,    uri: Module.resolve(id),    deps: deps,    factory: factory  }  // Try to derive uri in IE6-9 for anonymous modules  if (!meta.uri && doc.attachEvent) {    var script = getCurrentScript()    if (script) {      meta.uri = script.src    }    // 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.uri ? Module.save(meta.uri, meta) :    // Save information for "saving" work in the script onload event    anonymousMeta = meta}//生成mod,赋值一些基本属性// Save meta data to cachedModsModule.save = function(uri, meta) {  var mod = Module.get(uri)  // Do NOT override already saved modules  if (mod.status < STATUS.SAVED) {    mod.id = meta.id || uri    mod.dependencies = meta.deps || []    mod.factory = meta.factory    mod.status = STATUS.SAVED    emit("save", mod)  }}//创建mod,缓存到cachedMods// Get an existed module or create a new oneModule.get = function(uri, deps) {  return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))}//load执行一个匿名module// Use function is equal to load a anonymous moduleModule.use = function (ids, callback, uri) {  var mod = Module.get(uri, isArray(ids) ? ids : [ids])  mod._entry.push(mod)  mod.history = {}  mod.remain = 1  mod.callback = function() {    var exports = []    var uris = mod.resolve()    for (var i = 0, len = uris.length; i < len; i++) {      exports[i] = cachedMods[uris[i]].exec()    }    if (callback) {      callback.apply(global, exports)    }    delete mod.callback    delete mod.history    delete mod.remain    delete mod._entry  }  mod.load()}/*************************************************************    对外只暴露    Module.use(seajs.use)    Module.define(global.define) ************************************************************///获得mod的所有dep的uri // Resolve module.dependenciesModule.prototype.resolve = function() {  var mod = this  var ids = mod.dependencies  var uris = []  for (var i = 0, len = ids.length; i < len; i++) {    uris[i] = Module.resolve(ids[i], mod.uri)  }  return uris}//递归遍历自身依赖的子模板,判断全部加载完毕后执行onload// Load module.dependencies and fire onload when all doneModule.prototype.load = function() {  var mod = this  // If the module is being loaded, just wait it onload call  if (mod.status >= STATUS.LOADING) {    return  }  mod.status = STATUS.LOADING  // Emit `load` event for plugins such as combo plugin  var uris = mod.resolve()  emit("load", uris)  //将依赖uri遍历,判断是否全部加载  // Pass entry to it's dependencies  mod.pass(uris)  //如果全部加载,执行onload  // If module has entries not be passed, call onload  if (mod._entry.length) {    mod.onload()    return  }  // Begin parallel loading  var requestCache = {}  var m  //对依赖的模板执行拉取,拉取后执行load递归子依赖  for (var i = 0, len = uris.length; i < len; i++) {    m = cachedMods[uris[i]]    if (m.status < STATUS.FETCHING) {      m.fetch(requestCache)    }    //若模板已经拉取过,跳过拉取执行load    else if (m.status === STATUS.SAVED) {      m.load()    }  }  // Send all requests at last to avoid cache bug in IE6-9. Issues#808  for (var requestUri in requestCache) {    if (requestCache.hasOwnProperty(requestUri)) {      requestCache[requestUri]()    }  }}//将依赖uri遍历,判断是否全部加载Module.prototype.pass = function(uris) {  var mod = this  uris = uris || mod.resolve()  var len = uris.length  //use时mod._entry.push(mod)  for (var i = 0; i < mod._entry.length; i++) {    var entry = mod._entry[i]    var count = 0    for (var j = 0; j < len; j++) {      var m = Module.get(uris[j])      // If the module is unload and unused in the entry, pass entry to it      if (m.status < STATUS.LOADED && !entry.history.hasOwnProperty(m.uri)) {        entry.history[m.uri] = true        count++        m._entry.push(entry)        if(m.status === STATUS.LOADING) {          m.pass()        }      }    }    // If has passed the entry to it's dependencies, modify the entry's count and del it in the module    if (count > 0) {      entry.remain += count - 1      mod._entry.shift()      i--    }  }}//拉取模板,成功拉取后save并通知依赖自己的模板更新load// Fetch a moduleModule.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)  var requestUri = emitData.requestUri || uri  // Empty uri or a non-CMD module  if (!requestUri || fetchedList.hasOwnProperty(requestUri)) {    mod.load()    return  }  if (fetchingList.hasOwnProperty(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: data.charset  })  if (!emitData.requested) {    requestCache ?      requestCache[emitData.requestUri] = sendRequest :      sendRequest()  }  function sendRequest() {    seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset)  }  function onRequest(error) {    delete fetchingList[requestUri]    fetchedList[requestUri] = true    // Save meta data of anonymous module    if (anonymousMeta) {      Module.save(uri, anonymousMeta)      anonymousMeta = null    }    // Call callbacks    var m, mods = callbackList[requestUri]    delete callbackList[requestUri]    while ((m = mods.shift())) {      // When 404 occurs, the params error will be true      if(error === true) {        m.error()      }      else {        m.load()      }    }  }}//全部加载完触发onload,回调callback(seajs.use(id,callback))// Call this method when module is loadedModule.prototype.onload = function() {  var mod = this  mod.status = STATUS.LOADED  for (var i = 0, len = mod._entry.length; i < len; i++) {    var entry = mod._entry[i]    if (--entry.remain === 0) {      entry.callback()    }  }  delete mod._entry}//seajs.use->mod.onload->mod.callback()->mod.exec();// Execute a moduleModule.prototype.exec = function () {  var mod = this  // When module is executed, DO NOT execute it again. When module  // is being executed, just return `module.exports` too, for avoiding  // circularly calling  if (mod.status >= STATUS.EXECUTING) {    return mod.exports  }  mod.status = STATUS.EXECUTING  if (mod._entry && !mod._entry.length) {    delete mod._entry  }  //non-cmd module has no property factory and exports  if (!mod.hasOwnProperty('factory')) {    mod.non = true    return  }  // Create require  var uri = mod.uri  function require(id) {    var m = Module.get(require.resolve(id))    if (m.status == STATUS.ERROR) {      throw new Error('module was broken: ' + m.uri);    }    return m.exec()  }  require.resolve = function(id) {    return Module.resolve(id, uri)  }  require.async = function(ids, callback) {    Module.use(ids, callback, uri + "_async_" + cid())    return require  }  // Exec factory  var factory = mod.factory  var exports = isFunction(factory) ?    factory(require, mod.exports = {}, mod) :    factory  if (exports === undefined) {    exports = mod.exports  }  // Reduce memory leak  delete mod.factory  mod.exports = exports  mod.status = STATUS.EXECUTED  // Emit `exec` event  emit("exec", mod)  return exports}

 

 
 

 

转载于:https://www.cnblogs.com/yinshen/p/3190429.html

你可能感兴趣的文章
zoj 3703(背包)
查看>>
一种新的子波域滤波算法
查看>>
cookie之三天免登录代码
查看>>
1043 幸运号码 数位DP
查看>>
js18
查看>>
2018-2019-2 20175308实验一 《Java开发环境的熟悉》实验报告
查看>>
如何设置WIN7自动登录(去除登录密码)
查看>>
关于bash中if语法结构的广泛误解(转)
查看>>
10G整数文件中寻找中位数或者第K大数
查看>>
操作手机数据库的uri
查看>>
Python小应用1 - 抓取网页中的链接地址
查看>>
HTML表格和列表笔记&练习<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>关于表格的一些练...
查看>>
Hadoop HBase概念学习系列之hbase shell中执行java方法(高手必备)(二十五)
查看>>
数据类型
查看>>
SharePoint 2010中的内容类型集线器 - 内容类型发布与订阅
查看>>
如何解决在Windows Server 2008 R2 上安装证书服务重启后出现 CertificationAuthority 91错误事件...
查看>>
c# 获取键盘的输入
查看>>
mysql忘记密码
查看>>
小股神助A股股民畅享经济发展红利
查看>>
Python灰帽子pdf
查看>>