这次是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 fetched10 FETCHING: 1,11 // 2 - The meta data has been saved to cachedMods12 SAVED: 2,13 // 3 - The `module.dependencies` are being loaded14 LOADING: 3,15 // 4 - The module are ready to execute16 LOADED: 4,17 // 5 - The module is being executed18 EXECUTING: 5,19 // 6 - The `module.exports` is available20 EXECUTED: 621 }
接下来的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.dependenciesModule.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}
// 抓取一个moduleModule.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转化为uriModule.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 }}// 新建或取得一个moduleModule.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 APIseajs.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 Developersseajs.Module = Moduledata.fetchedList = fetchedListdata.cid = cidseajs.require = function(id) { var mod = Module.get(Module.resolve(id)) if (mod.status < STATUS.EXECUTING) { mod.onload() mod.exec() } return mod.exports}
写到后面才发现,原来这块的源码那么长,默默的决定再开一篇把剩下的运行顺序写写完。