seajs 源码分析三

这次是seajs源码分析的倒数第二篇,因为涉及到后面的代码运行分析,所以比较长(终于要写一篇超长博客了。。。)

首先还是和以前一样,对seajs的源码进行分析

先是一系列的变量,这些是在seajs中的全局变量,用于储存一些要加载或加载完的js,css文件的module,还有加载的状态,源码如下:

 1 var cachedMods = seajs.cache = {}
 2 var anonymousMeta
 3 
 4 var fetchingList = {}
 5 var fetchedList = {}
 6 var callbackList = {}
 7 
 8 var STATUS = Module.STATUS = {
 9   // 1 - The `module.uri` is being fetched
10   FETCHING: 1,
11   // 2 - The meta data has been saved to cachedMods
12   SAVED: 2,
13   // 3 - The `module.dependencies` are being loaded
14   LOADING: 3,
15   // 4 - The module are ready to execute
16   LOADED: 4,
17   // 5 - The module is being executed
18   EXECUTING: 5,
19   // 6 - The `module.exports` is available
20   EXECUTED: 6
21 }

接下来的Module构造函数就是加载js,css的核心函数,通过一些列的处理,实现文件的加载,加载过程中调用顺序在最后讲

function Module(uri, deps) {
  this.uri = uri
  this.dependencies = deps || []
  this.exports = null
  this.status = 0

  // 哪些依赖自己
  this._waitings = {}

  // 还未加载的依赖文件数
  this._remain = 0
}
Module.prototype.resolve = function() {
  var mod = this
  var ids = mod.dependencies
  var uris = []
  // 用循环将每个依赖文件路径整理出来,放入uris中
  for (var i = 0, len = ids.length; i < len; i++) {
    uris[i] = Module.resolve(ids[i], mod.uri)
  }
  return uris
}
// 加载module.dependencies
Module.prototype.load = function() {
  var mod = this

  // 如果这个module已经在加载了,那么就等待onload运行
  if (mod.status >= STATUS.LOADING) {
    return
  }

  mod.status = STATUS.LOADING

  // 运行load事件
  var uris = mod.resolve()
  emit("load", uris)

  var len = mod._remain = uris.length
  var m

  // 初始化module并且在waitings中注册
  for (var i = 0; i < len; i++) {
    m = Module.get(uris[i])

    if (m.status < STATUS.LOADED) {
      // 注意:这里可能不止一次的需求,所以不应该一直是1
      m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1
    }
    else {
      mod._remain--
    }
  }

  if (mod._remain === 0) {
    mod.onload()
    return
  }

  // 开始加载依赖的文件
  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()
    }
  }

  // 在最后一起发出请求,防止IE下的bug发生. Issues#808
  for (var requestUri in requestCache) {
    if (requestCache.hasOwnProperty(requestUri)) {
      requestCache[requestUri]()
    }
  }
}

// 在module加载完成后,运行此函数
Module.prototype.onload = function() {
  var mod = this
  mod.status = STATUS.LOADED

  if (mod.callback) {
    mod.callback()
  }

  // 让那些需要这个文件的文件module在remain ===0的情况下运行onload
  var waitings = mod._waitings
  var uri, m

  for (uri in waitings) {
    if (waitings.hasOwnProperty(uri)) {
      m = cachedMods[uri]
      // 注意减的数不是1,与上面设置对应
      m._remain -= waitings[uri]
      if (m._remain === 0) {
        m.onload()
      }
    }
  }

  //减少空间占用
  delete mod._waitings
  delete mod._remain
}
// 抓取一个module
Module.prototype.fetch = function(requestCache) {
  var mod = this
  var uri = mod.uri

  mod.status = STATUS.FETCHING

  // 运行fetch事件
  var emitData = { uri: uri }
  emit("fetch", emitData)
  var requestUri = emitData.requestUri || uri

  // 对于无uri情况
  if (!requestUri || fetchedList[requestUri]) {
    mod.load()
    return
  }
 // 如果fetchingList已经有了,那么将这个mod推入,然后等待即可
  if (fetchingList[requestUri]) {
    callbackList[requestUri].push(mod)
    return
  }

  fetchingList[requestUri] = true
  callbackList[requestUri] = [mod]

  // 运行request事件
  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() {
    delete fetchingList[requestUri]
    fetchedList[requestUri] = true

    // 为匿名module保存meta数据
    if (anonymousMeta) {
      Module.save(uri, anonymousMeta)
      anonymousMeta = null
    }

    // 运行callback
    var m, mods = callbackList[requestUri]
    delete callbackList[requestUri]
    while ((m = mods.shift())) m.load()
  }
}
// 运行module中的代码
Module.prototype.exec = function () {
  var mod = this

  // 当一个module已经运行过了,那么跳过
  // 防止循环不断运行
  if (mod.status >= STATUS.EXECUTING) {
    return mod.exports
  }

  mod.status = STATUS.EXECUTING

  // require函数
  var uri = mod.uri

  function require(id) {
    return Module.get(require.resolve(id)).exec()
  }

  require.resolve = function(id) {
    return Module.resolve(id, uri)
  }

  require.async = function(ids, callback) {
    Module.use(ids, callback, uri + "_async_" + cid())
    return require
  }

  // 运行factory函数
  // factory会在define中设置
  var factory = mod.factory

  var exports = isFunction(factory) ?
      factory(require, mod.exports = {}, mod) :
      factory

  if (exports === undefined) {
    exports = mod.exports
  }

  // 较少内存消耗
  delete mod.factory

  mod.exports = exports
  mod.status = STATUS.EXECUTED

  // 执行exec事件
  emit("exec", mod)

  return exports
}
// 将id转化为uri
Module.resolve = function(id, refUri) {
  // 激发resolve事件
  var emitData = { id: id, refUri: refUri }
  emit("resolve", emitData)
  // 调用的是seajs.resolve
  return emitData.uri || seajs.resolve(emitData.id, refUri)
}

// define函数,这个函数会被暴露到全局
Module.define = function (id, deps, factory) {
  var argsLen = arguments.length

  // 只有工厂函数的情况
  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
    }
  }

  // 通过分析factory的代码,得到需要加载的文件
  // 全文正则分析
  if (!isArray(deps) && isFunction(factory)) {
    deps = parseDependencies(factory.toString())
  }

  var meta = {
    id: id,
    uri: Module.resolve(id),
    deps: deps,
    factory: factory
  }

  // 对于匿名的module,尝试着从currentScript拿到uri
  if (!meta.uri && doc.attachEvent) {
    var script = getCurrentScript()

    if (script) {
      meta.uri = script.src
    }
  }

  // 激发define事件
  emit("define", meta)

  meta.uri ? Module.save(meta.uri, meta) :
      // 储存信息
      anonymousMeta = meta
}
// 将meta中的内容存到已缓存的module中
Module.save = function(uri, meta) {
  var mod = Module.get(uri)

  // 绝不覆盖已经存储过的module
  if (mod.status < STATUS.SAVED) {
    mod.id = meta.id || uri
    mod.dependencies = meta.deps || []
    mod.factory = meta.factory
    mod.status = STATUS.SAVED
  }
}

// 新建或取得一个module
Module.get = function(uri, deps) {
  return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
}

// 加载的函数
Module.use = function (ids, callback, uri) {
  var mod = Module.get(uri, isArray(ids) ? ids : [ids])
  // mod的callback在这里添加
  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()
}

//在所有其他module先加载预加载模块
Module.preload = function(callback) {
  var preloadMods = data.preload
  var len = preloadMods.length

  if (len) {
    Module.use(preloadMods, function() {
      // 移除已经加载的预加载模块
      preloadMods.splice(0, len)

      Module.preload(callback)
    }, data.cwd + "_preload_" + cid())
  }
  else {
    callback()
  }
}


// Public API

seajs.use = function(ids, callback) {
  Module.preload(function() {
    Module.use(ids, callback, data.cwd + "_use_" + cid())
  })
  return seajs
}

Module.define.cmd = {}
// 将define暴露到全局
global.define = Module.define


// For Developers

seajs.Module = Module
data.fetchedList = fetchedList
data.cid = cid

seajs.require = function(id) {
  var mod = Module.get(Module.resolve(id))
  if (mod.status < STATUS.EXECUTING) {
    mod.onload()
    mod.exec()
  }
  return mod.exports
}

 写到后面才发现,原来这块的源码那么长,默默的决定再开一篇把剩下的运行顺序写写完。

seajs 源码分析三,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。