日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
微前端框架是怎么導(dǎo)入加載子應(yīng)用的

微前端似乎是最近一個(gè)很火的話(huà)題,我們也即將使用在生產(chǎn)環(huán)境中,接下來(lái)會(huì)更新一系列微前端源碼分析、手寫(xiě)微前端文章

創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),臨城企業(yè)網(wǎng)站建設(shè),臨城品牌網(wǎng)站建設(shè),網(wǎng)站定制,臨城網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,臨城網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

廢話(huà)不多說(shuō),直接參考目前的微前端框架注冊(cè)子應(yīng)用模塊代碼

下面代碼,我指定的entry,就是子應(yīng)用的訪(fǎng)問(wèn)入口地址

微前端到底是怎么回事呢?  我畫(huà)了一張圖

我們今天不談其他的實(shí)現(xiàn)技術(shù)細(xì)節(jié),坑點(diǎn),就談?wù)w架構(gòu),這張圖就能完全解釋清楚

那么registerMicroApps,到底做了什么呢?

源碼解析下,只看重要部分今天:

lifeCycles是我們自己傳入的生命周期函數(shù)(這里先不解釋?zhuān)?,跟react這種框架一樣,微前端針對(duì)每個(gè)子應(yīng)用,也封裝了一些生命周期,如果你是小白,那我就用最簡(jiǎn)單的話(huà)告訴你,生命周期鉤子,其實(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è)邏輯判斷

 
 
 
 
  1. case 0:  
  2.           entry = app.entry, appappName = app.name;  
  3.           _b = configuration.singular, singular = _b === void 0 ? false : _b, _c = configuration.sandbox, sandbox = _c === void 0 ? true : _c, importEntryOpts = __rest(configuration, ["singular", "sandbox"]); 
  4.  return [4  
  5. /*yield*/  
  6.           , 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ù)),流程線(xiàn)很長(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)行了一層包裝,高可用使用

 
 
 
 
  1. function processTpl(tpl, baseURI) {  
  2. var scripts = [];  
  3. var styles = [];  
  4. var entry = null;  
  5. var template = tpl  
  6. /*  
  7.   remove html comment first  
  8.   */  
  9.   .replace(HTML_COMMENT_REGEX, '').replace(LINK_TAG_REGEX, function (match) {  
  10. /*  
  11.     change the css link  
  12.     */  
  13. var styleType = !!match.match(STYLE_TYPE_REGEX);  
  14. if (styleType) {  
  15. var styleHref = match.match(STYLE_HREF_REGEX);  
  16. var styleIgnore = match.match(LINK_IGNORE_REGEX);  
  17. if (styleHref) {  
  18. var href = styleHref && styleHref[2];  
  19. var newHref = href;  
  20. if (href && !hasProtocol(href)) {  
  21.           newHref = getEntirePath(href, baseURI);  
  22.         }  
  23. if (styleIgnore) {  
  24. return genIgnoreAssetReplaceSymbol(newHref);  
  25.         }  
  26.         styles.push(newHref);  
  27. return genLinkReplaceSymbol(newHref);  
  28.       }  
  29.     }  
  30. var preloadOrPrefetchType = match.match(LINK_PRELOAD_OR_PREFETCH_REGEX) && match.match(LINK_HREF_REGEX);  
  31. if (preloadOrPrefetchType) {  
  32. var _match$matchmatch = match.match(LINK_HREF_REGEX),  
  33.           _match$match2 = (0, _slicedToArray2["default"])(_match$match, 3),  
  34.           linkHref = _match$match2[2];  
  35. return genLinkReplaceSymbol(linkHref, true);  
  36.     }  
  37. return match;  
  38.   }).replace(STYLE_TAG_REGEX, function (match) {  
  39. if (STYLE_IGNORE_REGEX.test(match)) {  
  40. return genIgnoreAssetReplaceSymbol('style file');  
  41.     }  
  42. return match;  
  43.   }).replace(ALL_SCRIPT_REGEX, function (match) {  
  44. var scriptIgnore = match.match(SCRIPT_IGNORE_REGEX); // in order to keep the exec order of all javascripts  
  45. // if it is a external script  
  46. if (SCRIPT_TAG_REGEX.test(match) && match.match(SCRIPT_SRC_REGEX)) {  
  47. /* 
  48.       collect scripts and replace the ref  
  49.       */  
  50. var matchmatchedScriptEntry = match.match(SCRIPT_ENTRY_REGEX);  
  51. var matchmatchedScriptSrcMatch = match.match(SCRIPT_SRC_REGEX);  
  52. var matchedScriptSrc = matchedScriptSrcMatch && matchedScriptSrcMatch[2];  
  53. if (entry && matchedScriptEntry) {  
  54. throw new SyntaxError('You should not set multiply entry script!');  
  55.       } else {  
  56. // append the domain while the script not have an protocol prefix  
  57. if (matchedScriptSrc && !hasProtocol(matchedScriptSrc)) {  
  58.           matchedScriptSrc = getEntirePath(matchedScriptSrc, baseURI);  
  59.         }  
  60.         entryentry = entry || matchedScriptEntry && matchedScriptSrc;  
  61.       }  
  62. if (scriptIgnore) {  
  63. return genIgnoreAssetReplaceSymbol(matchedScriptSrc || 'js file');  
  64.       }  
  65. if (matchedScriptSrc) {  
  66. var asyncScript = !!match.match(SCRIPT_ASYNC_REGEX);  
  67.         scripts.push(asyncScript ? {  
  68.           async: true,  
  69.           src: matchedScriptSrc  
  70.         } : matchedScriptSrc);  
  71. return genScriptReplaceSymbol(matchedScriptSrc, asyncScript);  
  72.       }  
  73. return match;  
  74.     } else {  
  75. if (scriptIgnore) {  
  76. return genIgnoreAssetReplaceSymbol('js file');  
  77.       } // if it is an inline script  
  78. var code = (0, _utils.getInlineCode)(match); // remove script blocks when all of these lines are comments.  
  79. var isPureCommentBlock = code.split(/[\r\n]+/).every(function (line) {  
  80. return !line.trim() || line.trim().startsWith('//');  
  81.       });  
  82. if (!isPureCommentBlock) { 
  83.          scripts.push(match);  
  84.       }  
  85. return inlineScriptReplaceSymbol;  
  86.     }  
  87.   });  
  88.   scriptsscripts = scripts.filter(function (script) {  
  89. // filter empty script  
  90. return !!script;  
  91.   });  
  92. return {  
  93.     template: template,  
  94.     scripts: scripts,  
  95.     styles: styles,  
  96. // set the last script as entry if have not set  
  97.     entry: entry || scripts[scripts.length - 1]  
  98.   };  

最終返回了一個(gè)對(duì)象,此時(shí)已經(jīng)不是一個(gè)純html的字符串了,而是一個(gè)對(duì)象,而且腳本樣式都分離了

這個(gè)是框架幫我們處理的,必須要設(shè)置一個(gè)入口js文件

 
 
 
 
  1. // 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ò)多講解這塊)

 
 
 
 
  1. window.addEventListener('hashchange', reroute);  
  2. window.addEventListener('popstate', reroute);  
  3. // 攔截所有注冊(cè)的事件,以便確保這里的事件總是第一個(gè)執(zhí)行  
  4. const originalAddEventListener = window.addEventListener;  
  5. const originalRemoveEventListener = window.removeEventListener;  
  6. window.addEventListener = function (eventName, handler, args) {  
  7.     if (eventName && HIJACK_EVENTS_NAME.test(eventName) && typeof handler === 'function') {  
  8.         EVENTS_POOL[eventName].indexOf(handler) === -1 && EVENTS_POOL[eventName].push(handler); 
  9.     }  
  10.     return originalAddEventListener.apply(this, arguments);  
  11. };  
  12. window.removeEventListener = function (eventName, handler) {  
  13.     if (eventName && HIJACK_EVENTS_NAME.test(eventName) && typeof handler === 'function') {  
  14.         let eventList = EVENTS_POOL[eventName];  
  15.         eventList.indexOf(handler) > -1 && (EVENTS_POOL[eventName] = eventList.filter(fn => fn !== handler));  
  16.     }  
  17.     return originalRemoveEventListener.apply(this, arguments);  
  18. }; 

也是redux的中間件思想,劫持了事件,然后進(jìn)行派發(fā),優(yōu)先調(diào)用微前端框架的路由事件,然后進(jìn)行過(guò)濾展示子應(yīng)用:

 
 
 
 
  1. export function getAppsToLoad() {  
  2.     return APPS.filter(notSkipped).filter(withoutLoadError).filter(isntLoaded).filter(shouldBeActive);  

整個(gè)微前端的觸發(fā)流程


本文標(biāo)題:微前端框架是怎么導(dǎo)入加載子應(yīng)用的
網(wǎng)頁(yè)路徑:http://www.5511xx.com/article/dpgihii.html