自己边读变加了一些注释,理解了一下seajs3.0.0工作的流程。正则没有一个个去理解,插件模块也没看, 以后有时间了可以补充完整~
var list = events[name] || (events[name] = [])
以前自己写都是
if(!events[name]){ events[name]=[]; } var list=events[name];
囧
获取seajs的加载路径:
var stack; try { var up = new Error(); throw up; } catch (e) { // IE won't set Error.stack until thrown stack = e.stack.split('\n'); }
// at Error (native) <- Here's your problem // at http://localhost:8000/_site/dist/sea.js:2:4334 <- What we want // at http://localhost:8000/_site/dist/sea.js:2:8386 // at http://localhost:8000/_site/tests/specs/web-worker/worker.js:3:1
根据url加载模块,使用的是webworker全局环境下的importScripts方法
function requestFromWebWorker(url, callback, charset) { // Load with importScripts var error; try { importScripts(url); } catch (e) { error = e; } callback(error); } // For Developers seajs.request = requestFromWebWorker;
// 通过script标签加载脚本 function request(url, callback, charset) { var node = doc.createElement("script") if (charset) { var cs = isFunction(charset) ? charset(url) : charset if (cs) { node.charset = cs } } addOnload(node, callback, url) 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 } //添加加载完成之后的处理,包括报错、清除对象、移除dom等操作 function addOnload(node, callback, url) { var supportOnload = "onload" in node if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) onload(true) } } else { node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } } function onload(error) { // 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 (!data.debug) { head.removeChild(node) } // Dereference the node node = null callback(error) } } // For Developers seajs.request = request
Module类定义
//定义module类 function Module(uri, deps) { //模块路径 this.uri = uri //这是一个字符串数组,内容是该模块依赖模块的id列表 this.dependencies = deps || [] //这是该模块所依赖的模块的一个map key为id,value为Module对象 this.deps = {} // Ref the dependence modules //模块状态 this.status = 0 //用来辅助load的一个数组,load完成后会删掉 this._entry = [] }
模块缓存
var cachedMods = seajs.cache = {}
原型方法列表
// 获取模块中依赖模块的uri数组 Module.prototype.resolve // 一个辅助模块load过程的函数 Module.prototype.pass //加载并执行模块 Module.prototype.load //模块加载完成后触发 Module.prototype.onload //模块404时候触发 Module.prototype.error //执行模块 Module.prototype.exec //用来获取模块文件,每调用一次给一个缓存对象设置一个key value,value是一个函数,调用即开始加载文件
函数方法列表
//通过id获取文件uri Module.resolve = function(id, refUri) //用来定义一个模块 Module.define= function (id, deps, factory) //将id、依赖、 factory 、模块加载状态设置给缓存中的模块 Module.save= function(uri, meta) ////根据uri,从缓存中获取模块,或者以接收的uri和deps参数创建一个模块返回,并加入缓存 Module.get=function(uri, deps) //用来加载并运行模块 Module.use = function(ids, callback, uri)
1 // Public API 2 3 seajs.use = function(ids, callback) { 4 //use 实际上创建了一个id为data.cwd + "_use_" + cid()的模块,这个模块的依赖模块是待启动的模块 5 Module.use(ids, callback, data.cwd + "_use_" + cid()) 6 return seajs 7 } 8 9 Module.define.cmd = {} 10 global.define = Module.define 11 12 13 // For Developers 14 15 seajs.Module = Module 16 data.fetchedList = fetchedList 17 data.cid = cid 18 19 seajs.require = function(id) { 20 var mod = Module.get(Module.resolve(id)) 21 if (mod.status < STATUS.EXECUTING) { 22 mod.onload() 23 mod.exec() 24 } 25 return mod.exports 26 } 27 28 /** 29 * config.js - The configuration for the loader 30 */ 31 32 // The root path to use for id2uri parsing 33 data.base = loaderDir 34 35 // The loader directory 36 data.dir = loaderDir 37 38 // The loader's full path 39 data.loader = loaderPath 40 41 // The current working directory 42 data.cwd = cwd 43 44 // The charset for requesting files 45 data.charset = "utf-8" 46 47 // data.alias - An object containing shorthands of module id 48 // data.paths - An object containing path shorthands in module id 49 // data.vars - The {xxx} variables in module id 50 // data.map - An array containing rules to map module uri 51 // data.debug - Debug mode. The default value is false 52 53 seajs.config = function(configData) { 54 55 for (var key in configData) { 56 var curr = configData[key] 57 var prev = data[key] 58 59 // Merge object config such as alias, vars 60 if (prev && isObject(prev)) { 61 for (var k in curr) { 62 prev[k] = curr[k] 63 } 64 } 65 else { 66 // Concat array config such as map 67 if (isArray(prev)) { 68 curr = prev.concat(curr) 69 } 70 // Make sure that `data.base` is an absolute path 71 else if (key === "base") { 72 // Make sure end with "/" 73 if (curr.slice(-1) !== "/") { 74 curr += "/" 75 } 76 curr = addBase(curr) 77 } 78 79 // Set config 80 data[key] = curr 81 } 82 } 83 84 emit("config", configData) 85 return seajs 86 }
function require(id) { var m = mod.deps[id] || Module.get(require.resolve(id)) if (m.status == STATUS.ERROR) { throw new Error('module was broken: ' + m.uri); } return m.exec() }
// Exec factory var factory = mod.factory //执行factory的代码,这个时候才执行,懒执行 var exports = isFunction(factory) ? factory(require, mod.exports = {}, mod) : factory
Module.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 } } // 从function字符串中获取依赖模块id if (!isArray(deps) && isFunction(factory)) { deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString()) } var meta = { id: id, uri: Module.resolve(id), deps: deps, factory: factory } meta.uri ? Module.save(meta.uri, meta) : // Save information for "saving" work in the script onload event anonymousMeta = meta }
(为了方便理解,源码中一些辅助代码被我剪切掉了)