自己邊讀變加了一些註釋,理解了一下seajs3.0.0工作的流程。正則沒有一個個去理解,外掛模組也沒看, 以後有時間了可以補充完整~
事件系統中事件佇列的獲取&定義方法
var list = events[name] || (events[name] = [])
以前自己寫都是
if(!events[name]){ events[name]=[]; } var list=events[name];
囧
載入模組檔案的方法
webworker環境下載入模組檔案
獲取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;
瀏覽器js執行緒中非同步載入模組檔案
// 通過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類定義
//定義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)
暴露出來的API
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 }
常用API和特性的理解
seajs.use原理
seajs.use ("index.js")=Module.use(["index.js"],undefined,"http://xxxx.com/xxx/xxx/_use_i")
① 通過Module.get建立模組mod,
② 通過Module.prototype.load載入mod模組,為mode定義history、remain、callback屬性,設定_entry
Module.prototype.load中:
①通過Module.prototype.resolve函式獲取模組的依賴模組uri陣列。
Module.prototype.resolve會遍歷mod物件的dependencies中的模組id,挨個通過Module.resolve函式獲取其uri,最後返回陣列
② 遍歷上一步獲取的uri,通過Module.get獲取模組,給mod的deps屬性設定值,key為依賴模組id,value為依賴模組物件
④ 通過Module.prototype.fetch 獲取所有依賴模組的模組檔案
⑤ 對子模組重複以上過程
⑥mod模組載入完成後,執行onload回撥,在onload中執行Module.use的回撥callback,在callback中執行Module.prototype.exec來執行該模組
require、exports、module的注入&懶執行
Module.use的回撥callback中會執行模組的Module.prototype.exec ,返回module的exports物件。
這個過程中,若factory是function,則呼叫factory,引數傳入require,mod.exports,mod。
require:
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
可以看到,require的作用是從快取中獲取模組,然後exec。也就是說,只有require函式被執行的時候,模組程式碼才會被執行。這就是seajs的as lazy as posible ,懶執行。
global.define
define函式的作用是從引數中獲取 id 、dependences 、 factory,然後從模組快取中獲取模組物件,或者建立新模組加入快取,將id、dependences、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 }
(為了方便理解,原始碼中一些輔助程式碼被我剪下掉了)
最後附上我寫過註釋的原始碼