天天看點

qiankun 微前端_微前端方案 qiankun(實踐及總結)

作者:沉末_

連結:

https://juejin.im/post/5ed73b73e51d4578724e3fa4

什麼是微前端?

我們先來看兩個實際的場景:

1. 複用别的的項目頁面

通常,我們的背景項目都長這樣:

qiankun 微前端_微前端方案 qiankun(實踐及總結)

如果我們的項目需要開發某個新的功能,而這個功能另一個項目已經開發好,我們想直接複用時。PS:我們需要的隻是别人項目的這個功能頁面的「内容部分」,不需要别人項目的頂部導航和菜單。

一個比較笨的辦法就是直接把别人項目這個頁面的代碼拷貝過來,但是萬一别人不是

vue

開發的,或者說

vue

版本、

UI

庫等不同,以及别人的頁面加載之前操作(路由攔截,鑒權等)我們都需要拷貝過來,更重要的問題是,别人代碼有更新,我們如何做到同步更新。

長遠來看,代碼拷貝不太可行,問題的根本就是,我們需要做到讓他們的代碼運作在他們自己的環境之上,而我們對他們的頁面僅僅是“引用”。這個環境包括各種插件(

vue

vuex

vue-router

等),也包括加載前的邏輯(讀

cookie

,鑒權,路由攔截等)。私有

npm

可以共享元件,但是依然存在技術棧不同/UI 庫不同等問題。

2. 巨無霸項目的自由拆分組合

  • 代碼越來越多,打包越來越慢,部署更新麻煩,一些插件的更新和公共元件的修改需要考慮的更多,很容易牽一發而動全身
  • 項目太大,參與人員越多,代碼規範比較難管理,代碼沖突也頻繁。
  • 産品功能齊全,但是客戶往往隻需要其中的部分功能。剝離不需要的代碼後,需要獨立制定版本,獨立維護,增加人力成本。

舉個栗子,你們的産品有幾百個頁面,功能齊全且強大,客戶隻需要其中的部分頁面,而且需要你們提供源碼,這時候把所有代碼都給出去肯定是不可能的,隻能挑出來客戶需要,這部分代碼需要另外制定版本維護,就很浪費。

常見微前端方案

微前端的誕生也是為了解決以上兩個問題:

  1. 複用(嵌入)别人的項目頁面,但是别人的項目運作在他自己的環境之上。
  2. 巨無霸應用拆分成一個個的小項目,這些小項目獨立開發部署,又可以自由組合進行售賣。

使用微前端的好處:

  1. 技術棧無關,各個子項目可以自由選擇架構,可以自己制定開發規範。
  2. 快速打包,獨立部署,互不影響,更新簡單。
  3. 可以很友善的複用已有的功能子產品,避免重複開發。

目前微前端主要有兩種解決方案:

iframe

方案和

single-spa

方案

iframe

方案

iframe

大家都很熟悉,使用簡單友善,提供天然的

js/css

隔離,也帶來了資料傳輸的不便,一些資料無法共享(主要是本地存儲、全局變量和公共插件),兩個項目不同源(跨域)情況下資料傳輸需要依賴

postMessage

iframe

有很多坑,但是大多都有解決的辦法:

  1. 頁面加載問題

iframe

和首頁面共享連接配接池,而浏覽器對相同域的連接配接有限制,是以會影響頁面的并行加載,阻塞

onload

事件。每次點選都需要重新加載,雖然可以采用

display:none

來做緩存,但是頁面緩存過多會導緻電腦卡頓。「(無法解決)」

  1. 布局問題

iframe

必須給一個指定的高度,否則會塌陷。

解決辦法:子項目實時計算高度并通過

postMessage

發送給首頁面,首頁面動态設定

iframe

高度。有些情況會出現多個滾動條,使用者體驗不佳。

  1. 彈窗及遮罩層問題

彈窗隻能在

iframe

範圍内垂直水準居中,沒法在整個頁面垂直水準居中。

  • 解決辦法 1:通過與架構頁面消息同步解決,将彈窗消息發送給首頁面,首頁面來彈窗,對原項目改動大且影響原項目的使用。
  • 解決辦法 2:修改彈窗的樣式:隐藏遮罩層,修改彈窗的位置。
  1. iframe

    内的

    div

    無法全屏

彈窗的全屏,指的是在浏覽器可視區全屏。這個全屏指的是占滿使用者螢幕。

全屏方案,原生方法使用的是

Element.requestFullscreen()

,插件:vue-fullscreen。當頁面在

iframe

裡面時,全屏會報錯,且

dom

結構錯亂。

qiankun 微前端_微前端方案 qiankun(實踐及總結)
  1. 浏覽器前進/後退問題

iframe

和首頁面共用一個浏覽曆史,

iframe

會影響頁面的前進後退。大部分時候正常,

iframe

多次重定向則會導緻浏覽器的前進後退功能無法正常使用。并且

iframe

頁面重新整理會重置(比如說從清單頁跳轉到詳情頁,然後重新整理,會傳回到清單頁),因為浏覽器的位址欄沒有變化,

iframe

src

也沒有變化。

  1. iframe

    加載失敗的情況不好處理

非同源的

iframe

在火狐及

chorme

都不支援

onerror

事件。

  • 解決辦法 1:

    onload

    事件裡面判斷頁面的标題,是否

    404

    或者

    500

  • 解決辦法 2:使用

    try catch

    解決此問題,嘗試擷取

    contentDocument

    時将抛出異常。

解決辦法參考:stackoverflow 上的問題:Catch error if iframe src fails to load

single-spa

微前端方案

spa

單頁應用時代,我們的頁面隻有

index.html

這一個

html

檔案,并且這個檔案裡面隻有一個内容标簽

,用來充當其他内容的容器,而其他的内容都是通過

js

生成的。也就是說,我們隻要拿到了子項目的容器

和生成内容的

js

,插入到主項目,就可以呈現出子項目的内容。

我們隻需要拿到子項目的上面四個标簽,插入到主項目的

HTML

中,就可以在父項目中展現出子項目。

這裡有個問題,由于子項目的内容标簽是動态生成的,其中的

img/video/audio

等資源檔案和按需加載的路由頁面

js/css

都是相對路徑,在子項目的

index.html

裡面,可以正确請求,而在主項目的

index.html

裡面,則不能。

舉個例子,假設我們主項目的網址是

www.baidu.com

,子項目的網址是

www.taobao.com

,在子項目的

index.html

裡面有一張圖檔

qiankun 微前端_微前端方案 qiankun(實踐及總結)

,那麼這張圖檔的完整位址是

www.taobao.com/logo.jpg

,現在将這個圖檔的

img

标簽生成到了父項目的

index.html

,那麼圖檔請求的位址是

www.baidu.com/logo.jpg

,很顯然,父項目伺服器上并沒有這張圖。

解決思路:

  1. 這裡面的

    js/css/img/video

    等都是相對路徑,能否通過

    webpack

    打包,将這些路徑全部打包成絕對路徑?這樣就可以解決檔案請求失敗的問題。
  2. 能否手動(或借助

    node

    )将子項目的檔案全部拷貝到主項目伺服器上,

    node

    監聽子項目檔案有更新,就自動拷貝過來,并且按

    js/css/img

    檔案夾合并
  3. 能否像

    CDN

    一樣,一個伺服器挂了,會去其他伺服器上請求對應檔案。或者說伺服器之間的檔案共享,主項目上的檔案請求失敗會自動去子伺服器上找到并傳回。

通常做法是動态修改

webpack

打包的

publicPath

,然後就可以自動注入字首給這些資源。

single-spa

是一個微前端架構,基本原理如上,在上述呈現子項目的基礎上,還新增了

bootstrap

mount

unmount

等生命周期。

相對于

iframe

single-spa

讓父子項目屬于同一個

document

,這樣做既有好處,也有壞處。好處就是資料/檔案都可以共享,公共插件共享,子項目加載就更快了,缺點是帶來了

js/css

污染。

single-spa

上手并不簡單,也不能開箱即用,開發部署更是需要修改大量的

webpack

配置,對子項目的改造也非常多。

qiankun

方案

qiankun

是螞蟻金服開源的一款架構,它是基于

single-spa

的。他在

single-spa

的基礎上,實作了開箱即用,除一些必要的修改外,子項目隻需要做很少的改動,就能很容易的接入。如果說

single-spa

是自行車的話,

qiankun

就是個汽車。

微前端中子項目的入口檔案常見的有兩種方式:

JS entry

HTML entry

single-spa

采用的是

JS entry

,而

qiankun

既支援

JS entry

,又支援

HTML entry

JS entry

的要求比較苛刻:

(1)将

css

打包到

js

裡面

(2)去掉

chunk-vendors.js

(3)去掉檔案名的

hash

(4)将

single-spa

模式的入口檔案(

app.js

)放置到

index.html

目錄,其他檔案不變,原因是要截取

app.js

的路徑作為

publicPath

APP entry 優點 缺點

JS entry

可以配合

systemJs

,按需加載公共依賴(

vue

,

vuex

,

vue-router

等)
需要各種打包配置配合,無法實作預加載

HTML entry

打包配置無需做太多的修改,可以預加載 多一層請求,需要先請求到

HTML

檔案,再用正則比對到其中的

js

css

其實

qiankun

還支援

config entry

建議使用

HTML entry

,使用起來和

iframe

一樣簡單,但是使用者體驗比

iframe

強很多。

qiankun

請求到子項目的

index.html

之後,會先用正則比對到其中的

js/css

相關标簽,然後替換掉,它需要自己加載

js

并運作,然後去掉

html/head/body

等标簽,剩下的内容原樣插入到子項目的容器中 :

qiankun 微前端_微前端方案 qiankun(實踐及總結)

使用

qiankun

的好處:

  1. qiankun

    自帶

    js/css

    沙箱功能,

    singles-spa

    可以解決

    css

    污染,但是需要子項目配合
  2. single-spa

    方案隻支援

    JS entry

    的特點,限制了它隻能支援

    vue

    react

    angular

    等技術開發的項目,對一些

    jQuery

    老項目則無能為力。

    qiankun

    則沒有限制
  3. qiankun

    支援子項目預請求功能。

js

沙箱

js/css

污染是無法避免的,并且是一個可大可小的問題。就像一顆定時炸彈,不知道什麼時候會出問題,排查也麻煩。作為一個基礎架構,解決這兩個污染非常重要,不能僅憑“規範”開發。

js

沙箱的原理是子項目加載之前,對

window

對象做一個快照,子項目解除安裝時恢複這個快照,如圖:

qiankun 微前端_微前端方案 qiankun(實踐及總結)

那麼如何監測

window

對象的變化呢,直接将

window

對象進行一下深拷貝,然後深度對比各個屬性顯然可行性不高,

qiankun

架構采用的是

ES6

新特性,

proxy

代理方法。具體如何操作的,之前的文章有寫(連結在文末),就不再贅述。

但是

proxy

是不相容

IE11

的,為了相容,低版本

IE

采用了

diff

方法:淺拷貝

window

對象,然後對比每一個屬性。

css 沙箱

qiankun

css

沙箱的原理是重寫

HTMLHeadElement.prototype.appendChild

事件,記錄子項目運作時新增的

style/link

标簽,解除安裝子項目時移除這些标簽。

single-spa

方案中我用了換膚的思路來解決

css

污染:首先

css-scoped

解決大部分的污染,對于一些全局樣式,在子項目給

body/html

加一個唯一的

id/class

(正常開發部署用),然後這個全局的樣式前面加上這個

id/class

,而

single-spa

模式則在

mount

周期給

body/html

加上這個唯一的

id/class

,在

unmount

周期去掉,這樣就可以保證這個全局

css

隻對這個項目生效了。

這兩個方案的緻命點都在于無法解決多個子項目同時運作時的

css

污染,以及子項目對主項目的

css

污染。

雖然說兩個項目同時運作常見并不常見,但是如果想實作

keep-alive

,就需要使用

display: none

将子項目隐藏起來,子項目不需要解除安裝,這時候就會存在兩個子項目同時運作,隻不過其中一個對使用者不可見。

css

沙箱還有個思路就是将子項目的樣式局限到子項目的容器範圍内生效,這樣隻需要給不同的子項目不同的容器就可以了。但是這樣也會有新的問題,子項目中

append

body

的彈窗,樣式就無法生效。是以說樣式污染還需要制定規範才行,約定

class

命名字首。

微前端方案實踐

在我的前幾篇文章(連結在文末)中,

single-spa

qiankun

demo

已經實作了,開發部署流程也都有,接下來就是實踐出真知,用在實際項目中,才知道有那些坑。

改造已有的項目為

qiankun

子項目

由于我們是

vue

技術棧,是以我就以改造一個

vue

項目為例說明,其他的技術棧原理是一樣的。

  1. src

    目錄新增檔案

    public-path.js

  1. 修改

    index.html

    中項目初始化的容器,不要使用

    #app

    ,避免與其他的項目沖突,建議換成項目

    name

    的駝峰寫法
  2. 修改入口檔案

    main.js

主要改動是引入修改

publicPath

的檔案和

export

三個生命周期。

注意:

  • webpack

    publicPath

    值隻能在入口檔案修改,之是以單獨寫到一個檔案并在入口檔案最開始引入,是因為這樣做可以讓下面所有的代碼都能使用這個。
  • 路由檔案需要

    export

    路由資料,而不是執行個體化的路由對象,路由的鈎子函數也需要移到入口檔案。
  • mount

    生命周期,可以拿到父項目傳遞過來的資料,

    router

    用于跳轉到主項目/其他子項目的路由,

    store

    是父項目的執行個體化的

    Vuex

  1. 修改打包配置

    vue.config.js

    :

注:這個

name

預設從

package.json

擷取,可以自定義,隻要和父項目注冊時的

name

保持一緻即可。

這個配置主要就兩個,一個是允許跨域,另一個是打包成

umd

格式。為什麼要打包成

umd

格式呢?是為了讓

qiankun

拿到其

export

的生命周期函數。我們可以看下其打包後的

app.js

就知道了:

qiankun 微前端_微前端方案 qiankun(實踐及總結)

root

在浏覽器環境就是

window

,

qiankun

拿這三個生命周期,是根據注冊應用時,你給的

name

值,

name

不一緻則會導緻拿不到生命周期函數

qiankun 微前端_微前端方案 qiankun(實踐及總結)

子項目開發的一些注意事項

  1. 所有的資源(圖檔/音視訊等)都應該放到

    src

    目錄,不要放在

    public

    或 者

    static

資源放

src

目錄,會經過

webpack

處理,能統一注入

publicPath

。否則在主項目中會 404。

參考:vue-cli3 的官方文檔介紹:何時使用-public-檔案夾

暴露給運維人員的配置檔案

config.js

,可以放在

public

目錄,因為在

index.html

url

為相對連結的

js/css

資源,

qiankun

會給其注入字首。

  1. 請給

    axios

    執行個體添加攔截器,而不是

    axios

    對象

後續會考慮子項目共享公共插件,這時就需要避免公共插件的污染

  1. 避免

    css

    污染

元件内樣式的

css-scoped

是必須的。

對于一些插入到

body

的彈窗,無法使用

scoped

,請不要直接使用原

class

修改樣式,請添加自己的

class

,來修改樣式。

  1. 謹慎使用

    position:fixed

在父項目中,這個定位未必準确,應盡量避免使用,确有相對于浏覽器視窗定位需求,可以用

position: sticky

,但是會有相容性問題(IE 不支援)。如果定位使用的是

bottom

right

,則問題不大。

還有個辦法,位置可以寫成動态綁定

style

的形式:

  1. body

    document

    等綁定的事件,請在

    unmount

    周期清除

js

沙箱隻劫持了

window.addEventListener

,使用

document.body.addEventListener

或者

document.body.onClick

添加的事件并不會被沙箱移除,會對其他的頁面産生影響,請在

unmount

周期清除

qiankun

常見問題及解決方案

qiankun

常見報錯

  1. 子項目未

    export

    需要的生命周期函數
qiankun 微前端_微前端方案 qiankun(實踐及總結)

先檢查下子項目的入口檔案有沒有

export

生命周期函數,再檢查下子項目的打包,最後看看請求到的子項目的檔案對不對。

  1. 子項目加載時,容器未渲染好
qiankun 微前端_微前端方案 qiankun(實踐及總結)

檢查容器

div

是否是寫在了某個路由裡面,路由沒比對到所有未加載。如果隻在某個路由頁面加載子項目,可以在頁面的

mounted

周期裡面注冊子項目并啟動。

主項目路由隻能用

history

模式嗎?

由于

qiankun

是通過

location.pathname

值來判斷目前應該加載哪個子項目的,是以需要給每個子項目注入不同的路由

path

,而

hash

模式子項目路由跳轉不改變

path

,是以無影響,

history

模式子項目路由設定

base

屬性即可。

如果主項目使用

hash

模式,那麼得用

location.hash

值來判斷目前應該加載哪個子項目,并且子項目都得是

hash

模式,還需要給子項目所有的路由都添加一個字首,子項目的路由跳轉如果之前使用的是

path

也需要修改,用

name

跳轉則不用。

如果主項目是

hash

模式子項目為

history

模式,那麼跳轉到子項目之後,無法跳轉到另一個

history

模式的子項目,也無法回到主項目的頁面。

vue

項目

hash

模式改

history

模式也很簡單:

  1. new Router

    時設定

    mode

    history

qiankun 微前端_微前端方案 qiankun(實踐及總結)
  1. webpack

    打包的配置(

    vue.config.js

    ) :
qiankun 微前端_微前端方案 qiankun(實踐及總結)
  1. 一些資源會報 404,相對路徑改為絕對路徑:
    qiankun 微前端_微前端方案 qiankun(實踐及總結)
    改為
    qiankun 微前端_微前端方案 qiankun(實踐及總結)
    即可

css

污染問題及加載

bug

  1. qiankun

    隻能解決子項目之間的樣式互相污染,不能解決子項目的樣式污染主項目的樣式

主項目要想不被子項目的樣式污染,子項目是

vue

技術,樣式可以寫

css-scoped

,如果子項目是

jQuery

技術呢?是以主項目本身的

id/class

需要特殊一點,不能太簡單,被子項目比對到。

  1. 從子項目頁面跳轉到主項目自身的頁面時,主項目頁面的

    css

    未加載的

    bug

産生這個問題的原因是:在子項目跳轉到父項目時,子項目的解除安裝需要一點點的時間,在這段時間内,父項目加載了,插入了

css

,但是被子項目的

css

沙箱記錄了,然後被移除了。父項目的事件監聽也是一樣的,是以需要在子項目解除安裝完成之後再跳轉。我原本想在路由鈎子函數裡面判斷下,子項目是否解除安裝完成,解除安裝完成再跳轉路由,然而路由不跳轉,子項目根本不會解除安裝。

臨時解決辦法:先複制一下

HTMLHeadElement.prototype.appendChild

window.addEventListener

,路由鈎子函數

beforeEach

中判斷一下,如果目前路由是子項目,并且去的路由是父項目的,則還原這兩個對象.

路由跳轉問題

在子項目裡面如何跳轉到另一個子項目/主項目頁面呢,直接寫

或者用

router.push/router.replace

是不行的,原因是這個

router

是子項目的路由,所有的跳轉都會基于子項目的

base

。寫

連結可以跳轉過去,但是會重新整理頁面,使用者體驗不好。

解決辦法也比較簡單,在子項目注冊時将主項目的路由執行個體對象傳過去,子項目挂載到全局,用父項目的這個

router

跳轉就可以了。

但是有一丢丢不完美,這樣隻能通過

js

來跳轉,跳轉的連結無法使用浏覽器自帶的右鍵菜單(如圖:

Chrome

自帶的連結右鍵菜單)

qiankun 微前端_微前端方案 qiankun(實踐及總結)

項目通信問題

項目之間的不要有太多的資料依賴,畢竟項目還是要獨立運作的。通信操作需要判斷是否

qiankun

模式,做相容處理。

通過

props

傳遞父項目的

Vuex

,如果子項目是

vue

技術棧,則會很好用。假如子項目是

jQuery/react/angular

,就不能很好的監聽到資料的變化。

qiakun

提供了一個全局的

GlobalState

來共享資料。主項目初始化之後,子項目可以監聽到這個資料的變化,也能送出這個資料。

vue

項目之間資料傳遞還是使用共享父元件的

Vuex

比較友善,與其他技術棧的項目之間的通信使用

qiankun

提供的

GlobalState

子項目之間的公共插件如何共享

如果主項目和子項目都用到了同一個版本的

Vue/Vuex/Vue-Router

等,主項目加載一遍之後,子項目又加載一遍,就很浪費。

要想複用公共依賴,前提條件是子項目必須配置

externals

,這樣依賴就不會打包進

chunk-vendors.js

,才能複用已有的公共依賴。

按需引入公共依賴,有兩個層面:

  1. 沒有使用到的依賴不加載
  2. 大插件隻加載需要的部分,例如

    UI

    元件庫的按需加載、

    echarts/lodash

    的按需加載。

webpack

externals

是支援大插件的按需引入的:

subtract

可以通過全局

math

對象下的屬性

subtract

通路(例如

window['math']['subtract']

)。

single-spa

可以按需引入子項目的公共依賴

single-spa

是使用

systemJs

加載子項目和公共依賴的,将公共依賴和子項目一起配置到

systemJs

的配置檔案

importmap.json

,就可以實作公共依賴的按需加載:

qiankun

如何按需引入公共依賴

巨無霸應用的公共依賴和公共函數被太多的頁面使用,導緻更新和改動困難,使用微前端可以讓各個子項目獨立擁有自己的依賴,互不幹擾。而我們想要複用公共依賴,這與微前端的理念是相悖的。

是以我的想法是:父項目提供公共依賴,子項目可以自由選擇用或者不用。

這個也很好實作,父項目先加載好依賴,然後在注冊子項目時,将

Vue/Vuex/Vue-Router

等通過

props

傳過去,子項目可以選擇用或者不用。

主項目:

子項目:

這樣做不太可行,原因有兩個:

  1. 子項目獨立運作時,

    Vue-Router/Vuex

    這些依賴從哪裡來?子項目是隻部署一份的,既可以獨立運作,也可以被

    qiankun

    內建。
  2. 父項目隻能傳遞它自己已經有的依賴,如何确定子項目需要哪些依賴?不滿足按需引入的需求

配置

webpack

externals

之後,子項目獨立運作時,這些依賴的來源「有且僅有」

index.html

中的外鍊

script

标簽。

在這個前提下,子項目和主項目的

vue

版本一緻的情況下,使用同一份伺服器檔案。即使無法共享,也是可以做

http

緩存的。

那麼

qiankun

能否做到,某個依賴加載了之後,不再加載,直接複用呢?比如說子項目 A 請求了伺服器上的 2.6 版本

vue

,切換到子項目 B,B 項目也用了這個

vue

檔案,能否不再次加載,直接複用呢?

其實是可以的,可以看到

qiankun

将子項目的外鍊

script

标簽,内容請求到之後,會記錄到一個全局變量中,下次再次使用,他會先從這個全局變量中取。這樣就會實作内容的複用,隻要保證兩個連結的

url

一緻即可。

是以隻要子項目配置了

webpack

externals

,并在

index.html

中使用外鍊

script

引入這些公共依賴,隻要這些公共依賴在同一台伺服器上,便可以實作子項目的公共依賴的按需引入,一個項目使用了之後,另一個項目使用不再重複加載,可以直接複用這個檔案。

qiankun

更完美的按需引入

雖然

qiankun

不會重複請求相同

url

的公共依賴,但是這也僅比

http

緩存強了一丢丢。

有缺陷的地方在于:

  1. 主項目中的公共依賴沒有記錄到這個緩存中,也就不會被其他的項目複用
  2. 隻是沒有重複請求,還是需要重複執行一次。能否不執行,直接複用?。

    js

    沙箱在子項目解除安裝時,會移除

    window

    上新增的變量,而

    webpack

    externals

    恰恰是将這些公共依賴挂載在

    window

    上,能否看情況移除這些公共依賴?
  3. 相同版本的依賴會複用,版本不同但是使用無差别,能否做到也複用?(版本不同

    url

    也就不同,就不會複用)但是這裡可能會有一些疑問,既然使用無差别,為什麼不更新插件?

這些問題可能需要去改動

qiankun

的源碼。

jQuery

老項目的資源加載問題

子項目的内容标簽插到父項目的

index.html

後,其中的資源(

img/video/audio

等)路徑都是相對的,導緻資源無法正确顯示。上面我列舉了三種解決方案。

一般來說,

jQuery

項目是不經過

webpack

打包的,是以沒法通過修改

publicPath

來注入路徑字首。後面兩種方法操作起來比較麻煩,或者說我們應該「優先從架構本身」解決這個問題,而不是其他方法。是以我想了如下三種方案:

方案一:動态插入 标簽

html

有一個原生标簽

,這個标簽隻能放在

裡面,它的

href

屬性是一個

url

值。

mdn

位址: base 文檔根 URL 元素

設定了

标簽之後,頁面上所有的連結和

url

都基于它的

href

。例如頁面通路位址是

https://www.taobao.com

,設定

之後,頁面中原本的圖

qiankun 微前端_微前端方案 qiankun(實踐及總結)

的實際請求位址會變成

https://www.baidu.com/img/jQuery1.png

,頁面上的

連結:

,點選之後,頁面會跳轉到:

https://www.baidu.com/about

可以看到,

标簽和

webpack

publicPath

有一樣的效果,那麼能否在

jQuery

項目加載之前,把

jQuery

項目的位址賦給

标簽,然後插入到

?這樣就可以解決

jQuery

項目的資源加載問題。

做法也很簡單,在

qiankun

提供的

beforeLoad

生命周期,判斷目前是否是

jQuery

項目:

這樣做子項目資源可以正确加載,但是

标簽的威力太強大了,會導緻所有的路由無法正常跳轉,跳轉到其他的子項目時,

連結是基于

的,會跳轉到

jQuery

子項目的不存在的路由。解決了一個

bug

,又出現了新的

bug

,這樣是不行的。是以這個方案可行性特别小。

方案二:劫持标簽插入函數

這個方案分兩步:

  1. 對于

    HTML

    中已有的

    img/audio/video

    等标簽,

    qiankun

    支援重寫

    getTemplate

    函數,可以将入口檔案

    index.html

    中的靜态資源路徑替換掉
  2. 對于動态插入的

    img/audio/video

    等标簽,劫持

    appendChild

    innerHTML

    insertBefore

    等事件,将資源的相對路徑替換成絕對路徑

前面我們說到,對于子項目是

HTML entry

的,

qiankun

拿到入口檔案

index.html

之後,會用正則比對到

标簽及其内容,

中的

link/style/script/meta

等标簽,然後插入到父項目的容器中。

qiankun 微前端_微前端方案 qiankun(實踐及總結)

我們可以傳遞一個

getTemplate

函數,将圖檔的相對路徑轉為絕對路徑,它會在處理模闆時使用:

對于動态插入的标簽,劫持其插入

DOM

的函數,注入字首。

假如子項目動态插入一張圖:

Hello, render with jQuery

qiankun 微前端_微前端方案 qiankun(實踐及總結)

');   return Promise.resolve(); };

主項目劫持

jQuery

html

方法:

當然了,還有個簡單粗暴的寫法,給

jQuery

項目的圖檔路徑寫成絕對路徑,但是不建議這麼做,換個伺服器部署就不能用了。

方案三:給

jQuery

項目加上

webpack

打包

這個方案的可行性不高,都是陳年老項目了,沒必要這樣折騰。

老項目的資源加載總結

qiankun

本身就對接入

jQuery

多頁應用比較乏力,一般使用場景就是,一個大項目隻接入某個/某幾個頁面,這樣的話使用方案二比較合理。

qiankun

使用總結

  1. 隻有一個子項目時,要想啟用預加載,必須使用

    start({ prefetch: 'all' })

  2. js

    沙箱并不能解決所有的

    js

    污染,例如我用

    onclick

    addEventListener

    給 添加了一個點選事件,

    js

    沙箱并不能消除它的影響,是以說,還得靠代碼規範和自己自覺
  3. qiankun

    架構不太好實作

    keep-alive

    需求,因為解決

    css/js

    污染的辦法就是删除子項目插入的

    css

    标簽和劫持

    window

    對象,解除安裝時還原成子項目加載前的樣子,這與

    keep-alive

    相悖:

    keep-alive

    要求保留這些,僅僅是樣式上的隐藏。
  4. qiankun

    無法很好嵌入一些老項目

雖然

qiankun

支援

jQuery

老項目,但是似乎對「多頁應用」沒有很好的解決辦法。每個頁面都去修改,成本很大也很麻煩,但是使用

iframe

嵌入這些老項目就比較友善。

  1. 安全和性能的問題

qiankun

将每個子項目的

js/css

檔案内容都記錄在一個全局變量中,如果子項目過多,或者檔案體積很大,可能會導緻記憶體占用過多,導緻頁面卡頓。

另外,

qiankun

運作子項目的

js

,并不是通過

script

标簽插入的,而是通過

eval

函數實作的,

eval

函數的安全和性能是有一些争議的:MDN 的 eval 介紹

  1. 微前端調試時,每次都需要分别進入子項目和主項目運作和打包,非常麻煩,可以使用

    npm-run-all

    插件來實作:一個指令,運作所有項目。

其中

--parallel

參數表示并行,沒有這個參數則是等上一個指令執行完才會執行下一個指令。

結尾

不要對

iframe

抱有偏見,它也是微前端的一種實作方式,如果頁面上無彈窗、無全屏等操作,

iframe

也是很好用的。配置緩存和

cdn

加速,如果是内網通路,也不會很慢。

iframe

qiankun

可以并存,

jQuery

多頁應用使用

iframe

接入就挺好,什麼時候什麼場景該用哪種方案,具體情況具體分析。

最後,文章有什麼問題或錯誤歡迎指出,謝謝!

附錄

single-spa

qiankun

demo

如何實作以及部分原理淺析,可以看我的這三篇文章:

  1. 從 0 實作一個前端微服務(上)
  2. 從 0 實作一個 single-spa 的前端微服務(中)
  3. 從 0 實作一個 single-spa 的前端微服務(下)

PS: 第三篇文章是今年 3 月份寫的,裡面涉及的

qiankun

源碼是 1.0 版本,

qiankun

在 4 月份釋出了 2.0 版本,但是基本原理大緻沒變。

行業内其他前端團隊對微前端的看法和實踐:

  1. 每日優鮮供應鍊前端團隊微前端改造
  2. 微前端在美團外賣的實踐
  3. 前端微服務在位元組跳動的打磨與應用
  4. 微前端在小米 CRM 系統的實踐
  5. 标準微前端架構在螞蟻的落地實踐

qiankun

的線上案例

  1. https://tech.antfin.com/partners
  2. https://www.zstack.io/

愛心三連擊 ?

1、看到這裡了就點個在看支援下吧,你的在看是支援我創作的最大動力。

2、關注公衆号前端森林,定期為你推送新鮮幹貨好文

3、添加微信fs1263215592,拉你進技術交流群一起學習,當然歡迎随時找我聊騷 ?

qiankun 微前端_微前端方案 qiankun(實踐及總結)