<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何設(shè)計(jì)實(shí)現(xiàn)微前端框架-qiankun

          共 13674字,需瀏覽 28分鐘

           ·

          2021-05-22 18:01

          正文

          本文是第七屆微前端,前端早早聊第 42 場(chǎng),來(lái)自螞蟻金服體驗(yàn)技術(shù)部 qiankun 的核心貢獻(xiàn)者 - 方渙的分享 - 講稿簡(jiǎn)要整理版(完整版含演示請(qǐng)看錄播視頻和 PPT):


          一、自我介紹

          大家好,今天由我?guī)?lái)前端早早聊本次微前端專題的最后一場(chǎng),《如何設(shè)計(jì)實(shí)現(xiàn)微前端框架 - qiankun》。首先呢,我做一個(gè)簡(jiǎn)單的自我介紹。

          自我介紹

          我叫方渙,在一些平臺(tái)上,比如知乎和語(yǔ)雀上,用的就是左邊這個(gè)頭像。

          我現(xiàn)在是螞蟻金服體驗(yàn)技術(shù)部的一名前端工程師,和有知一起維護(hù)和迭代 qiankun。現(xiàn)在也正在做螞蟻內(nèi)部新一代的前端運(yùn)行時(shí)平臺(tái),正是基于微前端技術(shù)的。

          qiankun 簡(jiǎn)介

          首先開(kāi)始之前,我先給大家簡(jiǎn)單的介紹一下乾坤,大家可以在 <qiankun.umijs.org> 的官網(wǎng)上,看到 qiankun 的介紹和 qiankun 的文檔,誠(chéng)如這張官網(wǎng)截圖所示,我們對(duì) qiankun 的一句話定義就是:“可能是你見(jiàn)過(guò)最完善的微前端解決方案”,它有以下 3 個(gè)特點(diǎn):

          ? 簡(jiǎn)單。無(wú)論你是什么技術(shù)棧,你用的是什么 js 框架,你都可以使用 qiankun 來(lái)把你的子應(yīng)用接入到你的框架應(yīng)用中,接入非常簡(jiǎn)單,就像接入一個(gè) iframe 系統(tǒng)一樣。

          ? 完備。我們基于 single-spa 封裝了非常多的能力,把你在構(gòu)建微前端系統(tǒng)中遇到的一些問(wèn)題,如樣式隔離、沙箱、預(yù)加載等等這些你需要的能力全部?jī)?nèi)置到了 qiankun 里面,所以它是一個(gè)比較完備的微前端解決方案。

          ? 生產(chǎn)可用。到目前為止,已經(jīng)大概有 200+ 的應(yīng)用,使用 qiankun 來(lái)接入自己的微前端體系。我們?cè)谖浵亙?nèi)外受過(guò)了大量線上系統(tǒng)的考驗(yàn),所以它是一個(gè)值得信賴的生產(chǎn)可用的解決方案。

          qiankun 名字由來(lái)

          很多人可能會(huì)好奇 qiankun 這個(gè)名字是怎么來(lái)的。實(shí)際上源自于這句話:小世界有大乾坤。我們希望在微前端這個(gè)小世界里面,通過(guò) qiankun 這個(gè)框架鼓搗一個(gè)大乾坤

          二、QIANKUN 的誕生與設(shè)計(jì)理念

          在第一小節(jié),我會(huì)跟大家聊一聊 qiankun 的誕生,關(guān)于 qiankun 是怎么來(lái)的;然后聊一聊 qiankun 的設(shè)計(jì)理念,就是 qiankun 怎么看待微前端的,qiankun 認(rèn)為微前端最重要的是什么?

          緣起:一次統(tǒng)一上云戰(zhàn)役

          qiankun 的源起,實(shí)際上是一次統(tǒng)一上云戰(zhàn)役,不過(guò)這次戰(zhàn)役具體是怎么樣的并不重要。大家可以看到在這張圖里看到我們這次戰(zhàn)役所制作的控制臺(tái),我們通過(guò)紅框把它劃分成了好幾個(gè)部分,大家可以看到最頂上的導(dǎo)航頭、左邊的菜單、右邊的幫助引導(dǎo)以及中間的接入產(chǎn)品,這也就是我們子應(yīng)用的主體。

          控制臺(tái)結(jié)構(gòu)

          把控制臺(tái)抽象一下,它的結(jié)構(gòu)就是這個(gè)樣子的。

          其中頂部的導(dǎo)航頭、身份認(rèn)證和左邊的側(cè)邊欄、新手引導(dǎo)在內(nèi)的所有部分加起來(lái)就是我們的主應(yīng)用,或者說(shuō)是框架應(yīng)用。而中間那個(gè)框也就是應(yīng)用主體,應(yīng)用 body 部分,它就是我們接入的一系列子應(yīng)用,它可能是應(yīng)用 a、應(yīng)用 b 、應(yīng)用 c 等等一系列的應(yīng)用。

          我們把這一系列的應(yīng)用一起集成到了一個(gè)大的控制臺(tái)應(yīng)用里面,這就是我們這次所做的事情。而這件事情正好是微前端技術(shù)的一個(gè)典型應(yīng)用場(chǎng)景,所以 qiankun 就從這個(gè)項(xiàng)目開(kāi)始了它的第一步。

          我們需要一個(gè)“友好”的微前端方案

          對(duì)這個(gè)項(xiàng)目而言,我們所需要的微前端方案,可以概括為一個(gè) “友好” 的微前端解決方案,那怎么樣算友好的微前端方案呢?我們提煉出兩個(gè)要求:

          • 技術(shù)棧無(wú)關(guān)。就是說(shuō)我們的微前端方案不應(yīng)該限制技術(shù)棧,我們不應(yīng)該要求說(shuō)我們的子應(yīng)用是 React 技術(shù)棧、Vue 技術(shù)棧或者說(shuō) Angular 技術(shù)棧。除了不應(yīng)該限制技術(shù)棧,我們需要解除應(yīng)用之間的一些隱性依賴,因?yàn)橛械臅r(shí)候雖然我們沒(méi)有限制應(yīng)用的技術(shù)棧,但是我們的應(yīng)用之間很可能有一些耦合,這些耦合發(fā)生在很多方面,我們需要消滅這些耦合,才能做到技術(shù)棧無(wú)關(guān)。
          • 接入簡(jiǎn)單,用起來(lái)就像 iframe 一樣。大家知道 iframe 使用是非常簡(jiǎn)單的,我只要建一個(gè) iframe 標(biāo)簽,把我的頁(yè)面嵌進(jìn)去就可以了。如果我們的微前端方案能夠做到和 iframe 一樣簡(jiǎn)單,我們就可以盡可能的避免舊應(yīng)用改造。大家看到在我們之前的那次戰(zhàn)役中,也就是我們緣起里,我們需要把非常多的應(yīng)用接入到框架應(yīng)用、接入到控制臺(tái)里,這就涉及到大量的應(yīng)用改造,而我們希望把應(yīng)用改造的工作量降到最低,不然的話我們肯定非常疲憊。

          技術(shù)棧無(wú)關(guān)

          這也正是 qiankun 所認(rèn)為的微前端的核心價(jià)值。后面 qiankun 面臨的很多技術(shù)選擇和最終的實(shí)現(xiàn)方式,都是出于技術(shù)棧無(wú)關(guān)的出發(fā)點(diǎn)的。當(dāng)我們思考:“在很多的解決方案中,哪個(gè)方案被 qiankun 最后所選用”的時(shí)候,我們會(huì)想哪個(gè)方案是最技術(shù)棧無(wú)關(guān)的,最技術(shù)棧無(wú)關(guān)的方案,往往就是 qiankun 最后所選擇的方案。

          “技術(shù)棧無(wú)關(guān)”為什么重要

          這里舉一個(gè)例子說(shuō)明,假如我們要做一個(gè)統(tǒng)一控制臺(tái),我們不妨就把控制臺(tái)叫做 ABC,然后控制臺(tái)需要接入三個(gè)不同的項(xiàng)目:項(xiàng)目 A 是一個(gè)月前新建的,還處在積極迭代中。項(xiàng)目 B 可能是一年前建的,現(xiàn)在已經(jīng)缺乏維護(hù)。當(dāng)然最難的可能是項(xiàng)目 C,它可能是一個(gè)的三年前項(xiàng)目,現(xiàn)在已經(jīng)無(wú)人維護(hù)了。

          我列出這幾個(gè)項(xiàng)目的技術(shù)棧,大家會(huì)發(fā)現(xiàn)它們之間有很大的不同,可能項(xiàng)目 A 現(xiàn)在用著最新的 React 版本,用著最新的技術(shù)棧,甚至超前的用了 Webpack5 作為打包工具;與此同時(shí),項(xiàng)目 B 可能就是大家一年前的主流技術(shù)選型,而項(xiàng)目 C 已經(jīng)是非常舊的技術(shù)棧了。在這種情況下,如果我們微前端方案是技術(shù)棧相關(guān)的,那就勢(shì)必得把項(xiàng)目 B 做升級(jí),得把項(xiàng)目 C 做改造,而這樣的成本很多時(shí)候是我們?cè)谧?ABC 這個(gè)項(xiàng)目的時(shí)候無(wú)法接受的。再者隨著時(shí)間的正常推移,項(xiàng)目 B 也會(huì)變成無(wú)人維護(hù)的狀態(tài),項(xiàng)目 A 技術(shù)棧也終歸會(huì)陳舊。

          所以只有做到技術(shù)棧無(wú)關(guān),才能保證在過(guò)了很久之后,我們的統(tǒng)一控制臺(tái) ABC,還是能夠安穩(wěn)的繼續(xù)維護(hù)下去,還是能夠安穩(wěn)的接入新的項(xiàng)目。

          玉伯有一段話是這么點(diǎn)評(píng)微前端的:微前端的前提還是得有主體應(yīng)用,然后才有微組件或微應(yīng)用,解決的是可控體系下前端協(xié)同開(kāi)發(fā)問(wèn)題。這里開(kāi)發(fā)問(wèn)題有兩部分:一部分是空間分離帶來(lái)的協(xié)作,另一部分是時(shí)間延續(xù)帶來(lái)的升級(jí)維護(hù)。空間分離帶來(lái)的協(xié)作,需要微前端方案能夠提供獨(dú)立開(kāi)發(fā)、獨(dú)立部署的特性,來(lái)處理協(xié)作問(wèn)題。而時(shí)間帶來(lái)的延續(xù)其實(shí)是需要我們做到技術(shù)棧無(wú)關(guān),做到隨著時(shí)間的推移,當(dāng)我們技術(shù)棧陳舊的時(shí)候,還是能夠正常的接入我們的框架應(yīng)用。

          “技術(shù)棧無(wú)關(guān)”的價(jià)值

          可能大家都比較熟悉阿里使命:讓天下沒(méi)有難做的生意。而技術(shù)棧無(wú)關(guān)的價(jià)值,也正是 qiankun 口號(hào):讓天下沒(méi)有短命的控制臺(tái)。

          • 我們不限制接入前端的技術(shù)棧,做到接入范圍廣。
          • 我們能夠做到向后兼容,能兼容年久的應(yīng)用。
          • 在這個(gè)技術(shù)棧無(wú)關(guān)的架構(gòu)下,我們也能做到向前兼容,這個(gè)架構(gòu)是穩(wěn)定的、面向未來(lái)的。

          三、QIANKUN 的技術(shù)實(shí)現(xiàn)與選擇

          簡(jiǎn)單的講了一下 qiankun 所堅(jiān)持的理念之后,我們接著來(lái)聊一聊 qiankun 具體的技術(shù)實(shí)現(xiàn)和最終選擇使用什么方案來(lái)實(shí)現(xiàn)微前端。

          微前端框架面臨的兩大共性問(wèn)題

          實(shí)際上所有的微前端框架都面臨這兩大共性問(wèn)題。當(dāng)你解決了這兩大問(wèn)題之后,你的微前端框架的運(yùn)行時(shí),就已經(jīng)基本可用了。

          • 問(wèn)題一是應(yīng)用的加載與切換。包括路由的處理、應(yīng)用加載的處理和應(yīng)用入口的選擇。
          • 問(wèn)題二是應(yīng)用的隔離與通信。這是應(yīng)用已經(jīng)加載之后面臨的問(wèn)題,它們包括 JS 的隔離(也就是副作用的隔離)、樣式的隔離、也包括父子應(yīng)用和子子應(yīng)用之間的通信問(wèn)題。

          應(yīng)用路由與 Future State

          首先我們來(lái)看應(yīng)用路由的問(wèn)題。在微前端體系結(jié)構(gòu)下,路由的劃分往往是如圖這個(gè)樣子的,這也是一種比較簡(jiǎn)單的方案。

          我們主應(yīng)用加載了應(yīng)用 A 和應(yīng)用 B。這個(gè)時(shí)候?qū)τ谥鲬?yīng)用來(lái)講,我就有兩個(gè)路由 /a/*/b/* 。在路由 A 下我會(huì)加載應(yīng)用 A,在另一個(gè)路由 B 下加載應(yīng)用 B。

          那當(dāng)我在路由 /b/list 下重刷頁(yè)面的時(shí)候會(huì)發(fā)生什么過(guò)程?實(shí)際上我需要先加載我的主應(yīng)用,我的主應(yīng)用檢測(cè)到這是一個(gè) /b/ 打頭的路由,于是它知道我應(yīng)該去加載應(yīng)用 B,隨后去加載了應(yīng)用 B。到了此時(shí)應(yīng)用 B 接管了路由,它發(fā)現(xiàn)后面的路由是 /list ,于是它顯示出 /list 的正確頁(yè)面,總體上就是這么一個(gè)過(guò)程。

          如果你沒(méi)有做一些處理的話,在最早加載主應(yīng)用的時(shí)候,就會(huì)面臨 404 和路由報(bào)錯(cuò),因?yàn)檫@個(gè)時(shí)候應(yīng)用 B 的路由系統(tǒng)并沒(méi)有加載進(jìn)來(lái),你并不知道 /list 需要顯示什么。這實(shí)際上是懶加載就會(huì)面臨的問(wèn)題。早年 Angular 社區(qū)把這個(gè)問(wèn)題叫做 Future State。要解決這個(gè)問(wèn)題,比較簡(jiǎn)單的方式就是我們?nèi)ソ俪忠幌侣酚上到y(tǒng),做一些改造,同樣的,通過(guò)借助像 react-router 之類的路由庫(kù),你也可以選用它的動(dòng)態(tài)路由方案,這些都是可以的。

          社區(qū)已有的成熟方案

          在 qiankun 這里,我們就直接選用了社區(qū)成熟的方案 Single-SPA。Single-SPA 已經(jīng)劫持的路由,幫我們做好了加載這件事情,也幫我們做好了路由切換這件事情,在這個(gè)方面我們就沒(méi)有自己造輪子了。

          應(yīng)用接入:協(xié)議接入

          同時(shí)由于我們使用了 Single-SPA,它順便幫我們解決另一個(gè)問(wèn)題:就是應(yīng)用接入的問(wèn)題。

          什么樣的一個(gè)應(yīng)用能夠成為子應(yīng)用,能夠接入到我們的框架應(yīng)用里?由于我們需要技術(shù)棧無(wú)關(guān),所以我們希望接入是一個(gè) 協(xié)議接入。只要你的應(yīng)用實(shí)現(xiàn)了 bootstrapmountunmount 三個(gè)生命周期鉤子,有這三個(gè)函數(shù)導(dǎo)出,我們的框架應(yīng)用就可以知道如何加載這個(gè)子應(yīng)用

          這三個(gè)鉤子也正好是子應(yīng)用的生命周期鉤子。當(dāng)子應(yīng)用第一次掛載的時(shí)候,我們會(huì)執(zhí)行 bootstrap 做一些初始化,然后執(zhí)行 mount 將它掛載。如果你是一個(gè) React 技術(shù)棧的子應(yīng)用,你可能就在 mount 里面寫(xiě) ReactDOM.render ,把你的 ReactNode 掛載到真實(shí)的節(jié)點(diǎn)上,把應(yīng)用渲染出來(lái)。當(dāng)你應(yīng)用切換走的時(shí)候,我們會(huì)執(zhí)行 unmount 把應(yīng)用卸載掉,當(dāng)它再次回來(lái)的時(shí)候(典型場(chǎng)景:你從應(yīng)用 A 跳到應(yīng)用 B,過(guò)了一會(huì)兒又跳回了應(yīng)用 A),這個(gè)時(shí)候我們是不需要重新執(zhí)行一次所有的生命周期鉤子的,我們不需要從 bootstrap 開(kāi)始,我們會(huì)直接從 mount 階段繼續(xù),這就也做到了應(yīng)用的緩存。

          App Entry 抉擇

          在這個(gè)方面,qiankun 還面臨著另一些選擇,其中一個(gè)就是 App Entry 選擇,也就是如何設(shè)計(jì)子應(yīng)用的加載入口,在這個(gè)問(wèn)題上,我們有兩個(gè)地方需要面臨抉擇:一個(gè)是什么時(shí)候去組合,另一個(gè)是加載子應(yīng)用的入口是什么。

          組合時(shí)機(jī)選擇

          在組合時(shí)機(jī)的選擇上,我們有兩個(gè)選擇:第一個(gè)是在構(gòu)建時(shí),把主子應(yīng)用打包在一起,著實(shí)際上就是一種多包的方案。這個(gè)方案它的好處是構(gòu)建的時(shí)候可以做公共依賴的提取,但是它的缺點(diǎn)在于我們把主子應(yīng)用構(gòu)建方案和工具都耦合在一起了,這非常不靈活地,這樣也沒(méi)辦法做到動(dòng)態(tài)加載。所以在絕大部分情況下,我們都會(huì)選擇運(yùn)行時(shí)去組合,就是運(yùn)行的時(shí)候才去動(dòng)態(tài)的加載子應(yīng)用,把它加載、渲染到框架應(yīng)用里。

          應(yīng)用入口選擇:HTML Entry

          那我們子應(yīng)用的入口又如何選擇?qiankun 在這里選擇的是 HTML,就是以 HTML 作為入口。

          大家看了剛才的分享,其實(shí)已經(jīng)清楚了:加載子應(yīng)用用的時(shí)候,我們實(shí)際上是需要提供一份資源列表,在這份資源列表里,我們可能列出了這個(gè)子應(yīng)用使用了哪些 JS,有哪些 CSS。

          但是 qiankun 的第一選擇其實(shí)是 HTML 入口,就是提供一份 HTML 文件。因?yàn)檫@份 HTML 中其實(shí)包含了子應(yīng)用的所有信息。它包含網(wǎng)頁(yè)的結(jié)構(gòu),包含了一些元信息。大家可以看到在這份 HTML 里我們有 CSS、JS 鏈接、有應(yīng)用要掛載的根路由 root 的 DOM。這些信息是非常全面的,比單純你拿 JS 和 CSS 組成一份資源列表作為入口,要清晰和完整得多。同時(shí) HTML Entry 這樣的設(shè)計(jì),也使得我們?cè)诮尤胍恍├吓f應(yīng)用的時(shí)候,更加簡(jiǎn)單。

          應(yīng)用入口選擇

          這里做一個(gè)簡(jiǎn)單的對(duì)比,用了 HTML Entry 可以把子應(yīng)用和主應(yīng)用更加的解耦了。但是如果你是選擇資源列表或者 JS 作為子應(yīng)用的入口,那么子應(yīng)用掛載的時(shí)候,DOM 節(jié)點(diǎn)往往需要和主應(yīng)用作一個(gè)約定,這就產(chǎn)生了一定程度的耦合。

          應(yīng)用隔離:JS沙箱

          實(shí)際上剛才那些抉擇完了之后,尤其是當(dāng)我們借用了 Single-SPA 能力之后,我們已經(jīng)基本解決了應(yīng)用的加載與切換。我們接下來(lái)要解決的另一塊事情是應(yīng)用的隔離和通信。

          首當(dāng)其沖就是應(yīng)用的隔離問(wèn)題,隔離問(wèn)題最開(kāi)始我們就是要做 JS 的隔離。在這個(gè)問(wèn)題上,有的時(shí)候你不隔離其實(shí)也可以運(yùn)行的,只不過(guò)不隔離,直接裸用 Single-SPA 的話,一旦應(yīng)用之間的發(fā)生了沖突,你的應(yīng)用很可能就跑不下去了,所以 JS 沙箱大部分情況是必要的。

          再 qiankun 沙箱的實(shí)現(xiàn)里,我們會(huì)有兩個(gè)環(huán)境:一個(gè)代表的是外部的部分,就是我們的全局環(huán)境 Global Env,指的是你框架應(yīng)用所運(yùn)行的全局環(huán)境。而子應(yīng)用加載的時(shí)候,實(shí)際上是應(yīng)該跑在一個(gè)內(nèi)部的沙箱環(huán)境里面,就是這張圖上所表示的 Render Env。

          沙箱實(shí)現(xiàn)思路有兩條:其中最經(jīng)典的實(shí)踐思路其實(shí)是快照沙箱

          快照沙箱就是在應(yīng)用沙箱掛載和卸載的時(shí)候記錄快照,在應(yīng)用切換的時(shí)候依據(jù)快照恢復(fù)環(huán)境,

          當(dāng)我子應(yīng)用加載、啟動(dòng)了之后,此時(shí)的環(huán)境其實(shí)就是 Render Environment,是內(nèi)部沙箱環(huán)境里,這個(gè)時(shí)候我記錄一下當(dāng)時(shí)的快照狀態(tài)。而在我最后子應(yīng)用 unmount 的時(shí)候,我把當(dāng)前的環(huán)境和記錄的快照進(jìn)行一個(gè)對(duì)比,把它恢復(fù)回原來(lái)的全局狀態(tài)。這就是說(shuō)當(dāng)我應(yīng)用掛載了又卸載了,這個(gè)過(guò)程走了一遍之后,我當(dāng)前整個(gè) Windows 運(yùn)行環(huán)境恢復(fù)成原來(lái)的樣子,應(yīng)用內(nèi)部所做的修改,在它卸載的時(shí)候就會(huì)被恢復(fù),這是快照沙箱思路。

          舉個(gè)例子,假如我們?cè)?A 應(yīng)用運(yùn)行時(shí),追加了一個(gè)全局變量,我們說(shuō) window.a = 123 ,這個(gè)時(shí)候 window.a 就變成了 123 ,但是在應(yīng)用 A 卸載之后,快照還原, window.a 會(huì)被重新刪除,你在全局環(huán)境中并不會(huì)繼續(xù)看到 a 變量

          快照怎么打?其實(shí)也有兩種思路:

          一種是直接用 windows diff。把當(dāng)前的環(huán)境和原來(lái)的環(huán)境做一個(gè)比較,我們跑兩個(gè)循環(huán),把兩個(gè)環(huán)境作一次比較,然后去全量地恢復(fù)回原來(lái)的環(huán)境。

          另一種思路其實(shí)是借助 ES6 的 proxy 就是代理。通過(guò)劫持 window ,我們可以劫持到子應(yīng)用對(duì)全局環(huán)境的一些修改。當(dāng)子應(yīng)用往 window 上掛東西、修改東西和刪除東西的時(shí)候,我們可以把這個(gè)操作記錄下來(lái)。當(dāng)我們恢復(fù)回外面的全局環(huán)境的時(shí)候,我們只需要反向執(zhí)行之前的操作即可。比如我們?cè)谏诚鋬?nèi)部設(shè)了一個(gè)新的變量 window.a = 123 。那在離開(kāi)的時(shí)候,我們只需要把 a 變量刪了即可。

          快照沙箱這個(gè)思路也正是 qiankun1.0 所使用的思路,它相對(duì)完善,但是缺點(diǎn)在于無(wú)法支持多個(gè)實(shí)例,也就是說(shuō)我兩個(gè)沙箱不可能同時(shí)激活,我不能同時(shí)掛載兩個(gè)應(yīng)用,不然的話這兩個(gè)沙箱之間會(huì)打架,導(dǎo)致錯(cuò)誤。

          所以說(shuō)我們把目光又投向了另一條思路,我們讓子應(yīng)用里面的環(huán)境和外面的環(huán)境完全隔離。就如圖所示,我們的 A 應(yīng)用就活在 A 應(yīng)用的沙箱里面,B 應(yīng)用就活在 B 應(yīng)用的沙箱里面,兩者之間要不發(fā)生干擾,這個(gè)沙箱的實(shí)現(xiàn)思路其實(shí)也是通過(guò) ES6 的 proxy,也通過(guò)代理特性實(shí)現(xiàn)的。

          應(yīng)用隔離:樣式隔離

          無(wú)論是剛才的快照沙箱還是另外的代理沙箱,其實(shí)都可以解決 JS 之間的副作用沖突。解決完 JS 之后,我們接下來(lái)解決的是 CSS 之間的沖突。

          在微前端框架里所面臨的樣式?jīng)_突器就兩種:一種是主子應(yīng)用樣式?jīng)_突,你的主應(yīng)用和你的子應(yīng)用兩者之間樣式會(huì)發(fā)生沖突,另一種是子應(yīng)用之間樣式?jīng)_突,當(dāng)你掛載了應(yīng)用 A 又掛載了應(yīng)用 B 的時(shí)候,這兩個(gè)應(yīng)用是平級(jí)的,它們之間樣式也會(huì)發(fā)生沖突。

          樣式隔離:Dynamic Stylesheet

          qiankun 所做的第一件事情其實(shí)是動(dòng)態(tài)樣式表。當(dāng)你從子應(yīng)用 A 切換到子應(yīng)用 B 的時(shí)候,這個(gè)時(shí)候需要把子應(yīng)用的樣式表 A 的樣式給刪除,把子應(yīng)用 B 的樣式表給掛載。這樣就避免了子應(yīng)用 A 的樣式和子應(yīng)用 B 的樣式同時(shí)存在于這個(gè)項(xiàng)目中,就做到了最基本的隔離。當(dāng)你從應(yīng)用 A 切到應(yīng)用 B 的時(shí)候,你的樣式也會(huì)自然的從應(yīng)用 A 的樣式切換到應(yīng)用 B 的樣式,我們會(huì)自動(dòng)的幫你做應(yīng)用樣式的刪除和加載。當(dāng)然這樣做只僅僅能夠保證你在單應(yīng)用模式下(就是同時(shí)只能有一個(gè)應(yīng)用活躍的情況下)保證子應(yīng)用和子應(yīng)用之間的樣式不會(huì)沖突。

          當(dāng)然動(dòng)態(tài)樣式表實(shí)現(xiàn)之后,我們還是沒(méi)有解決主應(yīng)用和主應(yīng)用之間的樣式?jīng)_突。實(shí)際上這個(gè)問(wèn)題最好的手段還是通過(guò)一些工程化的手段來(lái)解,比如說(shuō) BEM,就是說(shuō)大家約定一下,你應(yīng)用 A 樣式就統(tǒng)一加一個(gè) a- 的前綴,應(yīng)用 B 樣式就統(tǒng)一加一個(gè) b- 的前綴,你的框架應(yīng)用也統(tǒng)一加個(gè)前綴。大家通過(guò)一些約定(比如約定大家都加上一個(gè)應(yīng)用名的前綴)來(lái)避免沖突,這是非常有效的一個(gè)方案。尤其是主子應(yīng)用之間的沖突,大部分情況下你只要保證主應(yīng)用的樣式做好改造,保證主應(yīng)用的樣式是很具象的規(guī)則,不會(huì)跟子應(yīng)用沖突,那主子應(yīng)用之間的沖突其實(shí)也解決了。不過(guò)這種方案終歸是依賴約定,往往容易出紕漏。

          樣式隔離:工程化手段

          當(dāng)前 css module 其實(shí)非常成熟一種做法,就是通過(guò)編譯生成不沖突的選擇器名。你只要把你想要避免沖突的應(yīng)用,通過(guò) css module (在構(gòu)建工具里加一些 css 預(yù)處理器即可實(shí)現(xiàn))就能很簡(jiǎn)單的做到。css module 構(gòu)建打包之后,應(yīng)用之間的選擇器名就不同了,也就不會(huì)相互沖突了。

          css-in-js 也是一種流行的方案,通過(guò)這種方式編碼的樣式也不會(huì)沖突,這幾個(gè)方案實(shí)現(xiàn)起來(lái)都不復(fù)雜,而且都非常行之有效。所以絕大部分情況下,大家手動(dòng)用工程化手段處理一下主子應(yīng)用之間的樣式?jīng)_突,就可以解決掉樣式隔離的問(wèn)題。

          樣式隔離方案:Shadow DOM

          當(dāng)然來(lái)到 qiankun2.0 之后,我們追加了一個(gè)新的選項(xiàng),叫作嚴(yán)格樣式隔離,不知道大家有沒(méi)有使用過(guò)。

          其實(shí)嚴(yán)格樣是隔離代表 Shadow DOM。Shadow DOM 是可以真正的做到 CSS 之間完全隔離的,在 Shadow Boundary 這個(gè)陰影邊界阻隔之下,主應(yīng)用的樣式和子應(yīng)用的樣式可以完完全全的切分開(kāi)來(lái)。

          但是絕大部分情況下,你還是不能無(wú)腦的開(kāi)啟嚴(yán)格樣式隔離的。原因之前的同學(xué)也已經(jīng)提到過(guò)一些了,比如說(shuō)你在使用一些彈窗組件的時(shí)候(彈窗很多情況下都是默認(rèn)添加到了 document.body )這個(gè)時(shí)候它就跳過(guò)了陰影邊界,跑到了主應(yīng)用里面,樣式就丟了。又比方說(shuō)你子應(yīng)用使用的是 React 技術(shù)棧,而 React 事件代理其實(shí)是掛在 document 上的,它也會(huì)出一些問(wèn)題。所以實(shí)踐里當(dāng)你開(kāi)啟 Shadow DOM 之后,當(dāng)你的子應(yīng)用可能會(huì)遇到一些奇怪的錯(cuò)誤,這些錯(cuò)誤需要你一一的去手動(dòng)修復(fù),這是比較累的一個(gè)過(guò)程。

          我們提供了 Shadow DOM 這么一種樣式隔離方式。但是實(shí)際使用中還是工程化的手段最為可靠、最為簡(jiǎn)單。當(dāng)然在允許的情況下,大家還是可以去嘗試開(kāi)啟嚴(yán)格樣式隔離,畢竟這才是真正的隔離。

          樣式隔離 RFC:runtime css transformer

          qiankun 還收到過(guò)一個(gè)提案,讓我們可以動(dòng)態(tài)運(yùn)行時(shí)地去改變 css。

          如圖,比如說(shuō)原來(lái)子應(yīng)用里面的樣式是這個(gè)樣子,那轉(zhuǎn)換之后,我們可以把它前面加一個(gè)限定,我們規(guī)定只有在 data-qiankun-app1 這個(gè)子應(yīng)用里面(這個(gè) div 就是包裹子應(yīng)用的最外層的容器), .main 這條樣式才能真正的生效。通過(guò)這樣一個(gè)運(yùn)行時(shí)的轉(zhuǎn)換,我們也能夠達(dá)到樣式隔離的目的。當(dāng)然這個(gè)方案的問(wèn)題在于這個(gè)轉(zhuǎn)換不是那么好做,首先我們?cè)黾恿艘欢ǖ倪\(yùn)行時(shí)開(kāi)銷,其次對(duì)于一些媒體查詢,對(duì)于一些動(dòng)畫(huà)很可能會(huì)出一些意料之外的情況。這個(gè) RFC 至今至今沒(méi)有合并,不知道大家對(duì)此有什么看法?歡迎來(lái)到 PR 下發(fā)表你的見(jiàn)解。

          在我們通過(guò) JS 沙箱解決 JS 之間的副作用;通過(guò)不管用工程化手段也好,動(dòng)態(tài)樣式也好,或者說(shuō)開(kāi)啟 Shadow DOM,解決掉 CSS 沖突之后,我們就要考慮應(yīng)用之間的通信問(wèn)題了。

          應(yīng)用通信:基于 URL

          其實(shí)有一種最樸素的通訊方案,就是基于 URL。前端有一種設(shè)計(jì)叫做 URL 中心設(shè)計(jì),就是說(shuō)你的 URL 完全決定了你頁(yè)面展示出來(lái)是什么樣子。

          假如我的應(yīng)用里有一個(gè)列表,有一個(gè)分頁(yè),當(dāng)你點(diǎn)下一頁(yè)的時(shí)候,是不是就產(chǎn)生了一個(gè)在第二頁(yè)的 query 參數(shù)?你可能會(huì)把這個(gè)參數(shù)同步到路由上,這樣你把這個(gè)鏈接分享給別人的時(shí)候,別人就能看到跟你一樣的頁(yè)面。

          我們其實(shí)完全可以把這種路由翻譯成看作是一個(gè)函數(shù)調(diào)用,比如說(shuō)這里的路由 b/function-log,query 參數(shù) data 是 aaa ,我們可以把這個(gè)路由 URL 理解為我在調(diào)用 B 應(yīng)用的 log 函數(shù),這就像一次函數(shù)調(diào)用一樣。當(dāng)我們從應(yīng)用 A 跳去應(yīng)用 B,對(duì)應(yīng)路由發(fā)生變化的時(shí)候,就是觸發(fā)了一次函數(shù)調(diào)用,觸發(fā)了一次通信。

          所以路由實(shí)際上也有通信的功能。這種通信方式是完全解耦的,但是缺點(diǎn)就是比較弱。

          應(yīng)用通信:發(fā)布/訂閱模型

          另一種應(yīng)用間通信的模型就是我們可以掛一個(gè)事件總線。應(yīng)用之間不直接相互交互,我都統(tǒng)一去事件總線上注冊(cè)事件、監(jiān)聽(tīng)事件,通過(guò)這么一個(gè)發(fā)布訂閱模型來(lái)做到應(yīng)用之間的相互通信。

          有趣的是,我們什么框架都不需要引入,什么第三方庫(kù)都不需要引入,這里我們有一個(gè)天然的事件總線:window 的 CustomEvent 。我們可以在 window 上監(jiān)聽(tīng)一個(gè)自定義事件,然后在任意地方派發(fā)一個(gè)自定義事件,我們可以天然的通過(guò)自定義事件來(lái)做到應(yīng)用之間相互通信。

          應(yīng)用通信:基于 props

          第三種方案其實(shí)是基于 props ,這也是我們比較熟悉的模式了。

          大家寫(xiě)過(guò) React 或 Vue 都會(huì)知道,我們?cè)趯?xiě)一個(gè) input 的時(shí)候,會(huì)把 valueonChange 兩個(gè) props 都傳給了底下的輸入框。這里我們也可以同樣做,我們主應(yīng)用是可以傳遞一些 props 給子用的。我們把 stateonGlobalStateChange (就是監(jiān)聽(tīng)函數(shù)),還有我們的 onChange (就是 setGlobalState )三個(gè)都傳給子應(yīng)用。我們基于 props 也就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的主子應(yīng)用之間通信。

          那當(dāng)我們這樣子實(shí)現(xiàn)了主子應(yīng)用之間通信之后,我們子應(yīng)用與子應(yīng)用之間通信怎么做?讓大家都跟主應(yīng)用通信就行了。子應(yīng)用和子應(yīng)用之間就不要再多加一條通信鏈路了,我們大家都基于 props 和主應(yīng)用通信,這樣也能解通信問(wèn)題。

          應(yīng)用通信

          qiankun 本身在 2.0 的時(shí)候提供了最簡(jiǎn)單的基于 props 通信的 API。

          在我們提供 API 之前,就有非常多的同學(xué)來(lái)問(wèn)我們應(yīng)該應(yīng)用和應(yīng)用之間怎么通信?我們當(dāng)時(shí)都沒(méi)有給出具體的解決方案,給出具體的 API 或者給出具體的實(shí)踐和引導(dǎo)。實(shí)際上在你的應(yīng)用需求、復(fù)雜度不同的時(shí)候,你應(yīng)該相應(yīng)的自己挑選自己適合的應(yīng)用間通信方案。

          對(duì) qiankun 來(lái)看,qiankun 關(guān)注的是你應(yīng)用和應(yīng)用之間是不是技術(shù)棧無(wú)關(guān)的,也就是說(shuō)應(yīng)用和應(yīng)用之間的耦合是不是盡可能弱的。從這個(gè)角度來(lái)講,如果我們基于自定義事件來(lái)做通信,那是一個(gè)非常弱的耦合。我不依賴什么特殊的東西,我只是借用了瀏覽器原生 API,技術(shù)實(shí)現(xiàn)總線,當(dāng)我脫離它的時(shí)候,我的應(yīng)用也是能夠自己獨(dú)立的工作的。而如果我們約定一個(gè) window 上的全局變量,或者說(shuō)我們做了一個(gè)全局 Redux,大家都去這個(gè)數(shù)據(jù)源消費(fèi)數(shù)據(jù),這個(gè)時(shí)候這個(gè)方案就可能是一個(gè)比較強(qiáng)耦合的方案。

          具體用什么樣的通信方案,還是需要根據(jù)大家的實(shí)際情況和面臨的業(yè)務(wù)場(chǎng)景自行決定。

          qiankun 沒(méi)有解答的工程與平臺(tái)問(wèn)題

          大家可以看到 qiankun 幫你做了很多事情,但是這個(gè)事情并沒(méi)有幫你一攬子全部做完,并不是用了 qiankun 之后,就完全地一勞永逸地解決了微前端的所有麻煩。#克軍 在 D2 的微前端的專場(chǎng)分享的時(shí)候,有提到過(guò)微前端的整個(gè)大圖,他把整個(gè)微前端體系分層了非常多的模塊,qiankun 所幫你解決的這一塊實(shí)際上是微前端的運(yùn)行時(shí)容器,這是整個(gè)微前端工程化里面的一個(gè)環(huán)節(jié)。

          從這個(gè)角度來(lái)講,qiankun 不算是一個(gè)完整的微前端解決方案,而是微前端運(yùn)行時(shí)容器的一個(gè)完整解決方案,當(dāng)你用了 qiankun 之后,你能解決幾乎所有的微前端運(yùn)行時(shí)容器的問(wèn)題,但是更多的一些涉及工程和平臺(tái)的問(wèn)題,

          你的版本管控、配置下發(fā)、監(jiān)控發(fā)布,安全檢測(cè)、等等這些怎么做,都不是 qiankun 作為一個(gè)庫(kù)所能解答的。這些問(wèn)題還是得看大家的具體情況,來(lái)選擇自己適合的解決方案。這也需要在微前端這方面做一些基礎(chǔ)設(shè)施建設(shè)的投入,才能比較好的解答工程問(wèn)題與平臺(tái)建設(shè)問(wèn)題。

          四、QIANKUN 的發(fā)展與未來(lái)

          聊了一下 qiankun 具體的一些技術(shù)實(shí)現(xiàn)的方案選擇,最后一節(jié)聊一下 qiankun 的發(fā)展情況,與 qiankun 未來(lái)會(huì)做成什么樣子。

          qiankun 歷程回顧

          先做一個(gè)簡(jiǎn)單的歷程回顧。

          • qiankun 是在去年 6 月份發(fā)布的 1.0 版本,那個(gè)時(shí)候全平臺(tái)的官宣文章可能是你見(jiàn)過(guò)最完善的微前端解決方案。
          • 在去年 12 月的時(shí)候,在 D2 技術(shù)論壇上,在微前端專場(chǎng)里面,大家可以找到有之的演講,標(biāo)準(zhǔn)微前端架構(gòu)在螞蟻的落地實(shí)踐。
          • 今年 4 月份的時(shí)候,我們發(fā)布了 2.0 版本。在這兩篇文章和演講中,大家可以更詳細(xì)的了解到 qiankun 設(shè)計(jì)的一些思考和 qiankun 的落地實(shí)踐。
          • 到今天為止,qiankun 大概已經(jīng)累積了 5.1k star。我們也受到了來(lái)自 Single-SPA 團(tuán)隊(duì)的肯定,我們也被非常多的團(tuán)隊(duì),比如說(shuō)像之前的飛豬團(tuán)隊(duì)選用為他們微前端體系中里面的一環(huán)。

          微前端的兩種形態(tài)

          實(shí)際上在最早 qiankun 1.0 發(fā)布的時(shí)候,我們就覺(jué)得微前端是有兩種形態(tài)的。

          在我們緣起所解決的業(yè)務(wù)場(chǎng)景里面,在 qiankun 第一次出場(chǎng)的時(shí)解決其實(shí)是一個(gè)單實(shí)例的場(chǎng)景。我們控制臺(tái)上同時(shí)運(yùn)行的只有一個(gè)應(yīng)用,我們會(huì)從應(yīng)用 A 切換到應(yīng)用 B,從應(yīng)用 B 切換到應(yīng)用 C,但我們不會(huì)同時(shí)把應(yīng)用 A 和應(yīng)用 B 都掛載在頁(yè)面上,這是一種單實(shí)例的場(chǎng)景。

          實(shí)際上我們覺(jué)得微前端還是有一種多實(shí)例場(chǎng)景。你在一個(gè)頁(yè)面里完全可以掛載多個(gè)微應(yīng)用,你可以同時(shí)把應(yīng)用 A、應(yīng)用 B、應(yīng)用 C 和應(yīng)用 D 合計(jì) 4 個(gè)應(yīng)用全部在同一個(gè)頁(yè)面里。

          這個(gè)時(shí)候就面臨一個(gè)問(wèn)題,應(yīng)用和組件有什么區(qū)別?這也很像之前的幾位同學(xué)所提到的,weiget 或者微模塊,因?yàn)樵谶@種情況下子應(yīng)用往往是沒(méi)有自己的路由的,與路由無(wú)關(guān),它確實(shí)在某種程度上很像一個(gè)組件,頁(yè)面的這一部分是一個(gè)組件或者說(shuō)是一個(gè)微應(yīng)用,表現(xiàn)出來(lái)的差距并不大。

          那怎么樣說(shuō)你是一個(gè)組件,又怎么樣說(shuō)你是一個(gè)微應(yīng)用?對(duì)我們來(lái)說(shuō),我們認(rèn)為能獨(dú)立開(kāi)發(fā),獨(dú)立部署,自己是能夠獨(dú)立完成一些功能的,其實(shí)就是一個(gè)微應(yīng)用。它和 weiget 或者說(shuō)微模塊能做到的事情是類似的,兩者之間并沒(méi)有明確的界限,比如說(shuō)你完全可以把一個(gè)組件升格為一個(gè)應(yīng)用,或者把一個(gè)應(yīng)用做成一個(gè)組件,這看你的具體場(chǎng)景和選擇。

          [email protected] 定位轉(zhuǎn)變

          當(dāng)我們 qiankun2.0 決定支持多應(yīng)用多實(shí)例場(chǎng)景的時(shí)候,實(shí)際上 qiankun 的定位也在悄然發(fā)生一定的轉(zhuǎn)變,這也正是 qiankun2.0,沒(méi)有改特別多的 API 保持了大部分兼容兼容的,卻說(shuō)是一次重大的變化的原因。

          我們?cè)仁且粋€(gè)基于路由的微前端框架,而現(xiàn)在實(shí)際上可以說(shuō)是一個(gè)微應(yīng)用加載器。你可以用 [email protected],更靈活的加載微應(yīng)用,我們提供了組件化的加載能力,用來(lái)支持做更復(fù)雜的應(yīng)用編排,并且支持更多的微前端應(yīng)用場(chǎng)景。

          [email protected] 加載微應(yīng)用

          我們給大家看一下代碼:如何用 qiankun2.0 來(lái)加載一個(gè)微應(yīng)用。

          我們提供提供了一個(gè)新的 API loadMicroApp ,通過(guò)這個(gè) API 可以加載一個(gè)微應(yīng)用。我們可以簡(jiǎn)單的基于該 API 封裝一下,封裝出一個(gè) MicroApp 的 React 組件。當(dāng)我拿到 MicroApp 組件之后,我們就可以很簡(jiǎn)單的使用它。

          比如說(shuō)我們可以和 react-router 這樣的路由庫(kù)一起使用,我們可以選擇在某一個(gè)路由上掛載組件,這個(gè)場(chǎng)景下就跟單實(shí)例的場(chǎng)景非常像,跟 [email protected] 一樣,是一個(gè)路由級(jí)別的使用場(chǎng)景。另外一種用法是我們是可以把它當(dāng)做一個(gè)組件使用,我們可以同時(shí)在一個(gè)頁(yè)面里掛載應(yīng)用 A 掛載應(yīng)用 B,我們可以在一個(gè)場(chǎng)景里掛載多個(gè)應(yīng)用,這是一種組件級(jí)的使用。

          那從單實(shí)例的場(chǎng)景轉(zhuǎn)到了多實(shí)例的場(chǎng)景,有什么用武之地?

          微應(yīng)用:混合研發(fā)

          有一個(gè)場(chǎng)景是混合研發(fā)。

          現(xiàn)在隨著前端的發(fā)展,其實(shí)很多人都在思考通過(guò)低代碼提效的問(wèn)題,我們已經(jīng)有一些比較成熟的低代碼平臺(tái)了,比如說(shuō)像云鳳蝶,你可以靠拖拖拽拽,做出一個(gè)好用的中后臺(tái)頁(yè)面,也有一些配置式的平臺(tái),就是你依靠配置一些 json、配置一些數(shù)據(jù),你就可以生成前端頁(yè)面

          但是與此同時(shí),我們也存在著低代碼平臺(tái)無(wú)法支持的一些,只能用 ProCode 來(lái)支撐的一些場(chǎng)景。很可能你一個(gè)應(yīng)用中同時(shí)存在著兩種場(chǎng)景,那這個(gè)時(shí)候你就可以把他們視為是不同的兩個(gè)微應(yīng)用。LowCode的微應(yīng)用和 ProCode 的微應(yīng)用。你可以把兩者同時(shí)放在一個(gè)項(xiàng)目里面,通過(guò) qiankun 去加載兩者來(lái)做到一種混合研發(fā)。這樣你可以同時(shí)享受 NoCode 或者 LowCode 的快速開(kāi)發(fā),又可以享受 ProCode 的完整能力,一旦你發(fā)現(xiàn)低代碼不符合你的需求的時(shí)候,你可以無(wú)縫地切換到 ProCode,留有后路。

          歡迎加入

          最后 qiankun 是非常歡迎大家參與共建,大家遇到什么問(wèn)題可以來(lái) Github 提 issue。大家可以加入我們 qiankun 的交流群,跟我們分享你的看法。未來(lái)的 qiankun3.0,也期待大家的加入和共建。

          書(shū)籍推薦

          最后一個(gè)環(huán)節(jié)是向大家推薦一本書(shū),我今天推薦的這本書(shū)是《松本行弘的程序世界》。通過(guò)這本書(shū)大家可以深入地理解程序設(shè)計(jì)背后的故事。

          QA

          我們的評(píng)論區(qū)非常火爆,提了非常多的問(wèn)題。因?yàn)闀r(shí)間的關(guān)系,我們只有 4 個(gè)機(jī)會(huì),大家可以關(guān)心一下自己的問(wèn)題是不是被回答到。

          請(qǐng)教方渙:qiankun 框架是否不關(guān)心不參與處理微應(yīng)用之間可能存在的共同依賴,怎么解決公共依賴的問(wèn)題?

          qiankun 本身是沒(méi)有幫助處理公共依賴的。這個(gè)事情上,可以通過(guò)一些構(gòu)建時(shí)候的工具來(lái)解決,比如說(shuō)像簡(jiǎn)單的 externals 和一些庫(kù),或者說(shuō)你可以嘗試 Webpack5 的聯(lián)邦模塊,或者說(shuō)是用一些 import map 之類的特性來(lái)做這件事情。qiankun 這方面也在探索路上,包括我們也再想自己如何和 Webpack5 的聯(lián)邦模塊結(jié)合,我們后續(xù)可能會(huì)出一些有關(guān)這方面的實(shí)踐。如果你有好的實(shí)踐歡迎提出來(lái)跟大家分享。但是同時(shí)我們有另一種觀點(diǎn),你真的需要把這些依賴提出來(lái)嗎?很多時(shí)候你的應(yīng)用之間的依賴庫(kù)的版本不同,這些不同的你可以認(rèn)為依賴庫(kù)其實(shí)也是應(yīng)用的一部分,你把它提出去之后,其實(shí)也是造成了你應(yīng)用之間的一些耦合。

          請(qǐng)教方渙:是否有對(duì)于子應(yīng)用服務(wù)異常時(shí),主應(yīng)用的容災(zāi)機(jī)制的推薦方法?

          這個(gè)我覺(jué)得是取決于具體場(chǎng)景和業(yè)務(wù)訴求的。因?yàn)樽討?yīng)用異常:一種可能是你代碼就寫(xiě)錯(cuò)了,子應(yīng)用掛了。這種情況下你主應(yīng)用可能只能感知到子應(yīng)用出一些異常故障,你知道這個(gè)子應(yīng)用掛了,你可以做一些上報(bào),或者嘗試做一些恢復(fù)。還有可能你是想做一些監(jiān)控實(shí)踐,你想捕獲子應(yīng)用里面運(yùn)行時(shí)的一些錯(cuò)誤情況,它是否發(fā)生了一些不到崩潰程度的一種報(bào)錯(cuò),你希望把它記錄下來(lái)。這方面其實(shí) qiankun 本身是不關(guān)注這個(gè)的,你完全可以自己來(lái)實(shí)現(xiàn)這些邏輯。

          請(qǐng)教方渙:目前 qiankun 是如何在原有的老項(xiàng)目中引入的?比如說(shuō)主應(yīng)用的路由是如何對(duì)接的?現(xiàn)在的樣式應(yīng)該是如何隔離?

          你的場(chǎng)景應(yīng)該是已經(jīng)有一個(gè)應(yīng)用 A,然后你想通過(guò) qiankun 把應(yīng)用 B 作為一個(gè)子應(yīng)用引入到你的應(yīng)用 A 中。在這個(gè)場(chǎng)景下,其實(shí)比較簡(jiǎn)單的方式是你在應(yīng)用 A 里面,就是你的主應(yīng)用里面,單獨(dú)分配一個(gè)路由給應(yīng)用 B。因?yàn)?qiankun 實(shí)際上是主應(yīng)用加載了之后,去檢查匹配子應(yīng)用是否激活。如果子應(yīng)用激活,我們就會(huì)把子應(yīng)用掛載,然后再去把路由交給自己的子應(yīng)用。所以這個(gè)場(chǎng)景下最簡(jiǎn)單的方式就是分配一個(gè)路由給你的子應(yīng)用。

          樣式隔離的問(wèn)題,其實(shí)剛才講了。可以有兩種方式:一種是使用一些工程化的手段,不管是約定前綴,或者說(shuō)使用 css module 做一些改造都可以避免主子應(yīng)用之間的樣式?jīng)_突。你也可以嘗試去開(kāi)啟嚴(yán)格的樣式隔離,使用Shadow DOM 的方式來(lái)讓子應(yīng)用樣式真正地做隔離,但是這個(gè)時(shí)候你的子應(yīng)用可能會(huì)遇到一些新的問(wèn)題需要你解決。

          請(qǐng)教方渙:qiankun 的子應(yīng)用是 jQuery 的老項(xiàng)目,老項(xiàng)目中圖片的地址是相對(duì)路徑,加載圖片時(shí)地址的域名和端口卻變成了主應(yīng)用的地址,應(yīng)該怎么解決?

          這個(gè)問(wèn)題可以到 Github 上提個(gè) issues 幫你看一看。


          瀏覽 136
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天爽夜夜爽精品成人免费 | 午夜精品少妇 | 殴美肏屄视频 | AV在线一区二区三区 | 亚洲精品一区二区三区在线观看 |