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

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

入口 seajs.use


seajs.use直接调用Module.use(),Module.use的源码如下:

// Use function is equal to load a anonymous module// ids:模块标识,uri是dirname + '_use_0' + 数字自增变量Module.use = function (ids, callback, uri) { // 从缓存cachedMods中获取已存在的模块对象或者新建module对象,并将缓存写入其中,返回当前模块对象  var mod = Module.get(uri, isArray(ids) ? ids : [ids])  // 对使用seajs.use或者require.async的模块挂在一个callback。seajs.use的callback在所有依赖都loaded完毕之后才执行,通过require递归的exec()各个已经loaded的依赖模块。这个callback作为seajs的入口,是所有模块的factory开始执行的线头起点。  mod.callback = function() {    var exports = []    // resolve通过正则表达式解析当前模块的依赖deps,并返回解析后的完成路径,其值为一个数组    var uris = mod.resolve()    // seajs.use入口处的依赖模块依次开始执行,此为模块执行的起点    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()}// Get an existed module or create a new oneModule.get = function(uri, deps) {  return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))}// Module构造函数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 unloaded dependencies  this._remain = 0 // 依赖的未加载完毕的模块个数}

load:加载当前模块的依赖,并且在当前模块的所有依赖都加载完毕后,触发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, mod)    // 初始化模块所依赖的未加载完毕的模块的个数  var len = mod._remain = uris.length  var m  // Initialize modules and register waitings  // 依次初始化当前mod依赖的deps为module对象,并且初始化_waitings,并存入cacheMods  for (var i = 0; i < len; i++) {    m = Module.get(uris[i])    if (m.status < STATUS.LOADED) {      // Maybe duplicate: When module has dupliate dependency, it should be it's count, not 1      // 如果当前依赖为未加载完成状态,则该模块的waiting + 1      m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1    } else {      // 当前依赖已经加载完成,则模块的remain - 1      mod._remain--    }  }    // 当前模块的所有依赖都已经加载完毕时,触发onload  if (mod._remain === 0) {    mod.onload()    return  }  // Begin parallel loading  var requestCache = {}  // 通过fetch函数,加载当前mod依赖的所有模块  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]()    }  }}// Call this method when module is loaded// 模块的所有依赖都加载完毕后,执行onloadModule.prototype.onload = function() {  var mod = this  mod.status = STATUS.LOADED    // 对于使用require.async定义的模块,有callback函数,在所有依赖模块加载完毕后执行callback函数  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) {        // 递归调用依赖当前模块的模块,执行onload函数,直到module.use顶端        m.onload()      }    }  }  // Reduce memory taken  delete mod._waitings  delete mod._remain}

fetch: fetch主要是通过创建<script async=true charset></script>标签并且append到head来实现依赖的加载。这里的依赖都是通过async来异步加载的,加载完毕之后立刻执行define函数,在模块文件执行完毕后(包括define和其他js代码),触发script的onload事件。


// 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  }  // fetchingList和callbackList为两个全局对象,分别存放正在加载的模块列表?????  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() {    seajs.request(emitData.requestUri, emitData.onRequest, emitData.charset)  }    // script标签的onload事件发生后,触发onRequest方法  function onRequest() {    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()))      m.load()  }}

request: 将script添加到head,并且定义onload事件


seajs.request = function (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, url)    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, 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) {      node.onload = onload      node.onerror = function() {        emit("error", {          uri: url,          node: node        })        onload()      }    } else {      node.onreadystatechange = function() {        if (/loaded|complete/.test(node.readyState)) {          onload()        }      }    }    function onload() {      // 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()    }  }

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 = undefined1 `1` // 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)    // Save information for "saving" work in the script onload event  meta.uri ? Module.save(meta.uri, meta) : anonymousMeta = meta}

resolve


// 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}// 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)}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}seajs.resolve = id2Uri;
参考文献

转载地址:http://hwgjx.baihongyu.com/

你可能感兴趣的文章
Redis实现分布式锁
查看>>
《The Cg Tutorial》阅读笔记——光照 Lighting
查看>>
镜像下载
查看>>
[Android] 关于getinstalledpackages参数的分析
查看>>
产品经理要掌握的数据知识:数据的基本概念、术语、指标,基本技术和分析方法...
查看>>
把二叉树转换成双向链表
查看>>
ELK 6.x 部署
查看>>
Toad for Oracle针对于Oracle数据库的可视化管理工具使用
查看>>
安装Gnome Library
查看>>
Sicily 4952. Another Rock-Paper-Scissors Problem 解题报告
查看>>
bzoj2212 [POI2011]Tree Rotations
查看>>
DOM 3
查看>>
super关键字: oc
查看>>
什么是虚拟DOM
查看>>
LeetCode-73-Set Matrix Zeroes
查看>>
I/O复用
查看>>
ZIP() 函数
查看>>
使用 wxFormBuiler 做wxPython UI
查看>>
【转】XNA窗口设置问题
查看>>
Charles抓包
查看>>