国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

seajs學習之模塊的依賴加載及模塊API的導出

2019-11-20 08:42:03
字體:
來源:轉載
供稿:網友

前言

SeaJS非常強大,SeaJS可以加載任意 JavaScript 模塊和css模塊樣式,SeaJS會保證你在使用一個模塊時,已經將所依賴的其他模塊載入到腳本運行環境中。

通過參照上文的demo,我們結合源碼分析在簡單的API調用的背后,到底使用了什么技巧來實現各個模塊的依賴加載以及模塊API的導出。

模塊類和狀態類

首先定義了一個Module類,對應與一個模塊

function Module(uri, deps) { this.uri = uri this.dependencies = deps || [] this.exports = null this.status = 0 // Who depends on me this._waitings = {} // The number of unloaded dependencies this._remain = 0}

Module有一些屬性,uri對應該模塊的絕對url,在Module.define函數中會有介紹;dependencies為依賴模塊數組;exports為導出的API;status為當前的狀態碼;_waitings對象為當前依賴該模塊的其他模塊哈希表,其中key為其他模塊的url;_remain為計數器,記錄還未加載的模塊個數。

var STATUS = Module.STATUS = { // 1 - The `module.uri` is being fetched FETCHING: 1, // 2 - The meta data has been saved to cachedMods SAVED: 2, // 3 - The `module.dependencies` are being loaded LOADING: 3, // 4 - The module are ready to execute LOADED: 4, // 5 - The module is being executed EXECUTING: 5, // 6 - The `module.exports` is available EXECUTED: 6}

上述為狀態對象,記錄模塊的當前狀態:模塊初始化狀態為0,當加載該模塊時,為狀態fetching;模塊加載完畢并且緩存在cacheMods后,為狀態saved;loading狀態意味著正在加載該模塊的其他依賴模塊;loaded表示所有依賴模塊加載完畢,執行該模塊的回調函數,并設置依賴該模塊的其他模塊是否還有依賴模塊未加載,若加載完畢執行回調函數;executing狀態表示該模塊正在執行;executed則是執行完畢,可以使用exports的API。

模塊的定義

commonJS規范規定用define函數來定義一個模塊。define可以接受1,2,3個參數均可,不過對于Module/wrappings規范而言,module.declare或者define函數只能接受一個參數,即工廠函數或者對象。不過原則上接受參數的個數并沒有本質上的區別,只不過庫在后臺給額外添加模塊名。

seajs鼓勵使用define(function(require,exports,module){})這種模塊定義方式,這是典型的Module/wrappings規范實現。但是在后臺通過解析工廠函數的require方法來獲取依賴模塊并給模塊設置id和url。

// Define a moduleModule.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 // 如果deps為非數組,則序列化工廠函數獲取入參。 if (!isArray(deps) && isFunction(factory)) { deps = parseDependencies(factory.toString()) } var meta = { id: id, uri: Module.resolve(id), // 絕對url deps: deps, factory: factory } // Try to derive uri in IE6-9 for anonymous modules // 導出匿名模塊的uri 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}

模塊定義的最后,通過Module.save方法,將模塊保存到cachedMods緩存體中。

parseDependencies方法比較巧妙的獲取依賴模塊。他通過函數的字符串表示,使用正則來獲取require(“…”)中的模塊名。

var REQUIRE_RE = /"(?://"|[^"])*"|'(?://'|[^'])*'|///*[/S/s]*?/*//|//(?:////|[^///r/n])+//(?=[^//])|////.*|/./s*require|(?:^|[^$])/brequire/s*/(/s*(["'])(.+?)/1/s*/)/gvar SLASH_RE = //////gfunction parseDependencies(code) { var ret = [] // 此處使用函數序列化(傳入的factory)進行字符串匹配,尋找require(“...”)的關鍵字 code.replace(SLASH_RE, "") .replace(REQUIRE_RE, function(m, m1, m2) { if (m2) {  ret.push(m2) } }) return ret}

異步加載模塊

加載模塊可以有多種方式,xhr方式可以同步加載,也可以異步加載,但是存在同源問題,因此難以在此使用。另外script tag方式在IE和現代瀏覽器下可以保證并行加載和順序執行,script element方式也可以保證并行加載但不保證順序執行,因此這兩種方式都可以使用。

在seajs中,是采用script element方式來并行加載js/css資源的,并針對舊版本的webkit瀏覽器加載css做了hack。

function request(url, callback, charset) { var isCSS = IS_CSS_RE.test(url) var node = doc.createElement(isCSS ? "link" : "script") if (charset) { var cs = isFunction(charset) ? charset(url) : charset if (cs) { node.charset = cs } } // 添加 onload 函數。 addOnload(node, callback, isCSS, url) if (isCSS) { node.rel = "stylesheet" node.href = url } else { 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}function addOnload(node, callback, isCSS, url) { var supportOnload = "onload" in node // for Old WebKit and Old Firefox if (isCSS && (isOldWebKit || !supportOnload)) { setTimeout(function() { pollCss(node, callback) }, 1) // Begin after node insertion return } if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) onload() } } else { node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } } function onload() { // 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 (!isCSS && !data.debug) { head.removeChild(node) } // Dereference the node node = null callback() }}// 針對 舊webkit和不支持onload的CSS節點判斷加載完畢的方法function pollCss(node, callback) { var sheet = node.sheet var isLoaded // for WebKit < 536 if (isOldWebKit) { if (sheet) { isLoaded = true } } // for Firefox < 9.0 else if (sheet) { try { if (sheet.cssRules) { isLoaded = true } } catch (ex) { // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR" // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0 // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR" if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") { isLoaded = true } } } setTimeout(function() { if (isLoaded) { // Place callback here to give time for style rendering callback() } else { pollCss(node, callback) } }, 20)}

其中有些細節還需注意,當采用script element方法插入script節點時,盡量作為首個子節點插入到head中,這是由于一個難以發現的bug:

GLOBALEVAL WORKS INCORRECTLY IN IE6 IF THE CURRENT PAGE HAS <BASE HREF> TAG IN THE HEAD

fetch模塊

初始化Module對象時,狀態為0,該對象對應的js文件并未加載,若要加載js文件,需要使用上節提到的request方法,但是也不可能僅僅加載該文件,還需要設置module對象的狀態及其加載module依賴的其他模塊。

這些邏輯在fetch方法中得以體現:

// Fetch a module// 加載該模塊,fetch函數中調用了seajs.request函數Module.prototype.fetch = function(requestCache) { var mod = this var uri = mod.uri mod.status = STATUS.FETCHING // Emit `fetch` event for plugins such as combo plugin var emitData = { uri: uri } emit("fetch", emitData) var requestUri = emitData.requestUri || uri // Empty uri or a non-CMD module if (!requestUri || fetchedList[requestUri]) { mod.load() return } if (fetchingList[requestUri]) { callbackList[requestUri].push(mod) return } fetchingList[requestUri] = true callbackList[requestUri] = [mod] // Emit `request` event for plugins such as text plugin 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 // Save meta data of anonymous module if (anonymousMeta) { Module.save(uri, anonymousMeta) anonymousMeta = null } // Call callbacks var m, mods = callbackList[requestUri] delete callbackList[requestUri] while ((m = mods.shift())) m.load() }}

其中seajs.request就是上節的request方法。onRequest作為回調函數,作用是加載該模塊的其他依賴模塊。

總結

以上就是seajs模塊的依賴加載及模塊API的導出的全部內容了,小編會在下一節,將介紹模塊之間依賴的加載以及模塊的執行。感興趣的朋友們可以繼續關注武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 蒲江县| 明溪县| 江口县| 台前县| 古交市| 西乌| 太原市| 临澧县| 原平市| 商城县| 河北省| 介休市| 闸北区| 宝山区| 房产| 深州市| 囊谦县| 永修县| 木兰县| 固原市| 西平县| 承德市| 兴安县| 仙居县| 邵阳市| 方山县| 永和县| 昆山市| 铜陵市| 湘潭市| 仁化县| 龙口市| 枝江市| 兴安县| 盐城市| 龙陵县| 巢湖市| 通江县| 洪洞县| 邯郸市| 阜阳市|