您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“微前端框架導(dǎo)入加載子應(yīng)用的方法是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“微前端框架導(dǎo)入加載子應(yīng)用的方法是什么”吧!
下面代碼,我指定的entry,就是子應(yīng)用的訪問(wèn)入口地址
微前端到底是怎么回事呢? 我畫(huà)了一張圖
我們今天不談其他的實(shí)現(xiàn)技術(shù)細(xì)節(jié),坑點(diǎn),就談?wù)w架構(gòu),這張圖就能完全解釋清楚
那么registerMicroApps,到底做了什么呢?
源碼解析下,只看重要部分今天:
lifeCycles是我們自己傳入的生命周期函數(shù)(這里先不解釋),跟react這種框架一樣,微前端針對(duì)每個(gè)子應(yīng)用,也封裝了一些生命周期,如果你是小白,那我就用最簡(jiǎn)單的話告訴你,生命周期鉤子,其實(shí)在框架源碼就是一個(gè)函數(shù)編寫(xiě)調(diào)用順序而已(有的分異步和同步)
apps就是我們傳入的數(shù)組,子應(yīng)用集合
代碼里做了一些防重復(fù)注冊(cè)、數(shù)據(jù)處理等
看源碼,不要全部都看,那樣很費(fèi)時(shí)間,而且你也得不到利益最大化,只看最精髓、重要部分
無(wú)論上面做了上面子應(yīng)用去重、數(shù)據(jù)處理,我只要盯著每個(gè)子應(yīng)用,即app這個(gè)對(duì)象即可
看到了loadApp這個(gè)方法,我們可以大概猜測(cè)到,是通過(guò)這個(gè)方法加載
下面__rest是對(duì)數(shù)據(jù)進(jìn)行處理
loadApp這個(gè)函數(shù)有大概300行,挑最重點(diǎn)地方看
registerApplication是single-spa的方法,我們這里通過(guò)loadApp這個(gè)方法,對(duì)數(shù)據(jù)進(jìn)行處理
上面這個(gè)函數(shù),應(yīng)該是整個(gè)微前端框架最復(fù)雜的地方,它最終會(huì)返回一個(gè)函數(shù),當(dāng)成函數(shù)傳遞給single-spa這個(gè)庫(kù)的registerApplication方法使用
它的內(nèi)部是switch case邏輯,然后返回一個(gè)數(shù)組
這是一個(gè)邏輯判斷
case 0: entry = app.entry, appappName = app.name; _b = configuration.singular, singular = _b === void 0 ? false : _b, _c = configuration.sandbox, sandbox = _c === void 0 ? true : _c, importEntryOpts = __rest(configuration, ["singular", "sandbox"]); return [4 /*yield*/ , importEntry(entry, importEntryOpts)];
重點(diǎn)來(lái)了
會(huì)通過(guò)importEntry 去加載entry(子應(yīng)用地址)
上面代碼里最重要的,如果我們entry傳入字符串,那么就會(huì)使用這個(gè)函數(shù)去加載HTML內(nèi)容(其實(shí)微前端的所有子應(yīng)用加載,都是把dom節(jié)點(diǎn)加載渲染到基座的index.html文件中的一個(gè)div標(biāo)簽內(nèi))
importHTML這個(gè)函數(shù),就是我們今晚最重要的一個(gè)點(diǎn)
傳入url地址,發(fā)起fetch請(qǐng)求(此時(shí)由于域名或者端口不一樣,會(huì)出現(xiàn)跨域,所有子應(yīng)用的熱更新開(kāi)發(fā)模式下,webpack配置要做以下處理,部署也要考慮這個(gè)問(wèn)題)
整個(gè)importHTML函數(shù)好像很長(zhǎng)很長(zhǎng),但是我們就看最重要的地方,一個(gè)框架(庫(kù)),流程線很長(zhǎng)+版本迭代原因,需要兼容老的版本,所以很多源碼對(duì)于我們其實(shí)是無(wú)用的
整個(gè)函數(shù),最后返回了一個(gè)對(duì)象,這里很明顯,通過(guò)fetch請(qǐng)求,獲取了對(duì)應(yīng)子應(yīng)用entry入口的資源文件后,轉(zhuǎn)換成了字符串
這里processTpl其實(shí)就是對(duì)這個(gè)子應(yīng)用的dom模版(字符串格式)進(jìn)行一個(gè)數(shù)據(jù)拼裝,其實(shí)也不是很復(fù)雜,由于時(shí)間關(guān)系,可以自己看看過(guò)程,重點(diǎn)看結(jié)果
這里的思想,是redux的中間件源碼思想,將數(shù)據(jù)進(jìn)行了一層包裝,高可用使用
function processTpl(tpl, baseURI) { var scripts = []; var styles = []; var entry = null; var template = tpl /* remove html comment first */ .replace(HTML_COMMENT_REGEX, '').replace(LINK_TAG_REGEX, function (match) { /* change the css link */ var styleType = !!match.match(STYLE_TYPE_REGEX); if (styleType) { var styleHref = match.match(STYLE_HREF_REGEX); var styleIgnore = match.match(LINK_IGNORE_REGEX); if (styleHref) { var href = styleHref && styleHref[2]; var newHref = href; if (href && !hasProtocol(href)) { newHref = getEntirePath(href, baseURI); } if (styleIgnore) { return genIgnoreAssetReplaceSymbol(newHref); } styles.push(newHref); return genLinkReplaceSymbol(newHref); } } var preloadOrPrefetchType = match.match(LINK_PRELOAD_OR_PREFETCH_REGEX) && match.match(LINK_HREF_REGEX); if (preloadOrPrefetchType) { var _match$matchmatch = match.match(LINK_HREF_REGEX), _match$match3 = (0, _slicedToArray2["default"])(_match$match, 3), linkHref = _match$match3[2]; return genLinkReplaceSymbol(linkHref, true); } return match; }).replace(STYLE_TAG_REGEX, function (match) { if (STYLE_IGNORE_REGEX.test(match)) { return genIgnoreAssetReplaceSymbol('style file'); } return match; }).replace(ALL_SCRIPT_REGEX, function (match) { var scriptIgnore = match.match(SCRIPT_IGNORE_REGEX); // in order to keep the exec order of all javascripts // if it is a external script if (SCRIPT_TAG_REGEX.test(match) && match.match(SCRIPT_SRC_REGEX)) { /* collect scripts and replace the ref */ var matchmatchedScriptEntry = match.match(SCRIPT_ENTRY_REGEX); var matchmatchedScriptSrcMatch = match.match(SCRIPT_SRC_REGEX); var matchedScriptSrc = matchedScriptSrcMatch && matchedScriptSrcMatch[2]; if (entry && matchedScriptEntry) { throw new SyntaxError('You should not set multiply entry script!'); } else { // append the domain while the script not have an protocol prefix if (matchedScriptSrc && !hasProtocol(matchedScriptSrc)) { matchedScriptSrc = getEntirePath(matchedScriptSrc, baseURI); } entryentry = entry || matchedScriptEntry && matchedScriptSrc; } if (scriptIgnore) { return genIgnoreAssetReplaceSymbol(matchedScriptSrc || 'js file'); } if (matchedScriptSrc) { var asyncScript = !!match.match(SCRIPT_ASYNC_REGEX); scripts.push(asyncScript ? { async: true, src: matchedScriptSrc } : matchedScriptSrc); return genScriptReplaceSymbol(matchedScriptSrc, asyncScript); } return match; } else { if (scriptIgnore) { return genIgnoreAssetReplaceSymbol('js file'); } // if it is an inline script var code = (0, _utils.getInlineCode)(match); // remove script blocks when all of these lines are comments. var isPureCommentBlock = code.split(/[\r\n]+/).every(function (line) { return !line.trim() || line.trim().startsWith('//'); }); if (!isPureCommentBlock) { scripts.push(match); } return inlineScriptReplaceSymbol; } }); scriptsscripts = scripts.filter(function (script) { // filter empty script return !!script; }); return { template: template, scripts: scripts, styles: styles, // set the last script as entry if have not set entry: entry || scripts[scripts.length - 1] }; }
最終返回了一個(gè)對(duì)象,此時(shí)已經(jīng)不是一個(gè)純html的字符串了,而是一個(gè)對(duì)象,而且腳本樣式都分離了
這個(gè)是框架幫我們處理的,必須要設(shè)置一個(gè)入口js文件
// set the last script as entry if have not set
下面是真正的single-spa源碼,注冊(cè)子應(yīng)用,用apps這個(gè)數(shù)組去收集所有的子應(yīng)用(數(shù)組每一項(xiàng)已經(jīng)擁有了腳本、html、css樣式的內(nèi)容)
此時(shí)我們只要根據(jù)我們之前編寫(xiě)的activeRule和監(jiān)聽(tīng)前端路由變化去控制展示子應(yīng)用即可,原理如下:(今天不做過(guò)多講解這塊)
window.addEventListener('hashchange', reroute); window.addEventListener('popstate', reroute); // 攔截所有注冊(cè)的事件,以便確保這里的事件總是第一個(gè)執(zhí)行 const originalAddEventListener = window.addEventListener; const originalRemoveEventListener = window.removeEventListener; window.addEventListener = function (eventName, handler, args) { if (eventName && HIJACK_EVENTS_NAME.test(eventName) && typeof handler === 'function') { EVENTS_POOL[eventName].indexOf(handler) === -1 && EVENTS_POOL[eventName].push(handler); } return originalAddEventListener.apply(this, arguments); }; window.removeEventListener = function (eventName, handler) { if (eventName && HIJACK_EVENTS_NAME.test(eventName) && typeof handler === 'function') { let eventList = EVENTS_POOL[eventName]; eventList.indexOf(handler) > -1 && (EVENTS_POOL[eventName] = eventList.filter(fn => fn !== handler)); } return originalRemoveEventListener.apply(this, arguments); };
也是redux的中間件思想,劫持了事件,然后進(jìn)行派發(fā),優(yōu)先調(diào)用微前端框架的路由事件,然后進(jìn)行過(guò)濾展示子應(yīng)用:
export function getAppsToLoad() { return APPS.filter(notSkipped).filter(withoutLoadError).filter(isntLoaded).filter(shouldBeActive); }
整個(gè)微前端的觸發(fā)流程
到此,相信大家對(duì)“微前端框架導(dǎo)入加載子應(yīng)用的方法是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。