Seajs是一款模塊化開發框架,遵循CMD規範。雖然到如今爲止不少模塊打包工具比它更加的完善,但仍是有必要拜讀一下的,畢竟爲前端模塊化的發展作了很大的貢獻,分析一下漲漲姿式。
文章主要從如下幾個方面來分析。有不對的地方,歡迎你們指出。前端
CMD(Common Module Definition)是seajs在推廣中規範出來的。詳情請看 CMD模塊定義規範node
先在瀏覽器控制檯中打印下seajs這個全局變量,能夠看到它掛載的一些對象和方法。讓咱們先對它有個總體的感覺
seajs 從use函數開始,到加載module的過程大體以下:
模塊加載完後瀏覽器會立馬執行define函數,這個函數比較有意思,先看源代碼git
// Resolve id to uri Module.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) } // Define a module 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 } } // 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) meta.uri ? Module.save(meta.uri, meta) : // Save information for "saving" work in the script onload event anonymousMeta = meta }
有意思的是若是factory是個函數,同時deps不是一個數組,那麼會將factory序列化,而後經過正則匹配從其中解析出依賴github
var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g var SLASH_RE = /\\\\/g function parseDependencies(code) { var ret = [] code.replace(SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) { ret.push(m2) } }) return ret }
解析依賴後會扔到模塊緩存系統中。以前模塊加載的時候就能夠看到,主模塊加載完以後會執行factory函數。在執行factory函數的時候,會執行require加載的依賴。而require函數會判斷模塊狀態是否已經執行過了,若是不是那麼就加載依賴,加載完後執行依賴。最後將執行的結果暴露給exports對象。
到此,seajs的整個加載執行過程已經分析完畢。相比requirejs,seajs代碼不是不少,可是可以感覺到代碼組織起來的精妙之處。web
seajs解決模塊的互相依賴是經過緩存系統和模塊的狀態來實現的。前端工程化
// `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0 // ref: // - https://bugs.webkit.org/show_activity.cgi?id=38995 // - https://bugzilla.mozilla.org/show_bug.cgi?id=185236 // - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events var isOldWebKit = +navigator.userAgent .replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+).*/, "$1") < 536
解決的辦法是經過一個定時器,去監聽link節點是否已經加載,而且解析完。api
function getCurrentScript() { if (currentlyAddingScript) { return currentlyAddingScript } // For IE6-9 browsers, the script onload event may not fire right // after the script is evaluated. Kris Zyp found that it // could query the script nodes and the one that is in "interactive" // mode indicates the current script // ref: http://goo.gl/JHfFW if (interactiveScript && interactiveScript.readyState === "interactive") { return interactiveScript } var scripts = head.getElementsByTagName("script") for (var i = scripts.length - 1; i >= 0; i--) { var script = scripts[i] if (script.readyState === "interactive") { interactiveScript = script return interactiveScript } } }
解決辦法是經過腳本的readyState===‘interactive’來判斷數組
以上就是對seajs的一個大體的分析,若有錯誤,歡迎指出。瀏覽器