基于Umi探索微前端以及其融合方案

寫在前面

“微前端”這個概念已經(jīng)在前端圈火了很長一段時間了,關(guān)于什么是微前端,微前端干了什么,其和傳統(tǒng)iframe嵌套有啥區(qū)別等等一系列疑問,社區(qū)的介紹也不少,為了保證各位同學(xué)讀到本文干貨時還保持著精力,這里對于以上概念將不做詳細贅述,當(dāng)然,為了照顧沒有了解過微前端的同學(xué),還是要做一個簡單的描述。做完概念描述后,我會用demo的方式,從基礎(chǔ)微前端到微前端的融合方案(重點!)做一個詳細講解。

概念簡答

什么是微前端

微前端是一種多個團隊通過獨立發(fā)布功能的方式來共同構(gòu)建現(xiàn)代化 web 應(yīng)用的技術(shù)手段及方法策略。

也就是說,一個“大項目”由一個個獨立的小項目拼起來的,或者說把一個“大項目”拆成了若干的小項目,獨立開發(fā)。并且其與技術(shù)棧無關(guān),說到這里,很多人就會想到iframe,確實,通過iframe可以滿足上面我們所說的一些功能特點,但其實,我們使用的微前端是一個叫qiankun的庫,當(dāng)然了,也有人說iframe是最早的微前端實現(xiàn),那我們姑且先這么認為吧。實際上,這種說法也是有一些道理的,那么為什么我們不使用iframe呢?
這里有一篇文章,有興趣的可以看一下。大概意思就是:

  • url 不同步。瀏覽器刷新 iframe url 狀態(tài)丟失、后退前進按鈕無法使用。
  • UI 不同步,DOM 結(jié)構(gòu)不共享。
  • 全局上下文完全隔離,內(nèi)存變量不共享。
  • 慢。每次子應(yīng)用進入都是一次瀏覽器上下文重建、資源重新加載的過程。

關(guān)注過qiankun的同學(xué),應(yīng)該看得出來,上面的知識點,都搬運自官網(wǎng),如果你已經(jīng)很熟悉了,那就可以直接跳過這段了~

什么場景下適合使用微前端

關(guān)于這樣的概念和理解,我想社區(qū)中已經(jīng)數(shù)不勝數(shù)了,那么按照我的理解就是:“當(dāng)你遇到了超大型巨無霸中后臺系統(tǒng)需要優(yōu)化拆分時、當(dāng)你遇到了一個需要多個團隊維護各自模塊堆積起來的項目時”,那么請讓微前端帶你來爽一把吧。

OK~概念性的廢話就說到這,下面我將手摸手帶大家搭建一個微前端項目,并對其融合方案做研究。

搭建基礎(chǔ)微前端環(huán)境

環(huán)境準備

為簡單起見,我們直接使用umi的qiankun插件plugin-qiankun,所以,你的項目中,只需要能跑起來umi即可。
我們知道,一個微前端,是由幾個小的模塊組成的一個大的系統(tǒng),一般的,我們會把這幾個小的模塊用一個負責(zé)layout的應(yīng)用包裹,我們管這個“盒子”叫主應(yīng)用,管下面一個一個的小模塊叫子應(yīng)用。
下面,我們就使用umi作為腳手架,一個一個大分別搭建三個項目呢,其中一個為“主應(yīng)用”,另外兩個為“主應(yīng)用”

搭建項目
yarn create @umijs/umi-app

談笑間,三個項目已經(jīng)搭建完畢
其中主項目采用ant design pro模板,依賴為umi3+antd4
兩個子項目采用umi app 模板,依賴為umi3 + antd4
為了快速搭建環(huán)境,按個應(yīng)用我使用了相同的技術(shù)棧,因為umi3在集成微前端上非常方便,當(dāng)然了,大多數(shù)情況下,微前端的子應(yīng)用之間都是采用的不同的技術(shù)棧,那時候可能要大家去踩踩坑了,現(xiàn)在社區(qū)關(guān)于微前端的知識還是比較豐富的,相比解決問題比較容易,當(dāng)然了,熱心的我,也為大家提供了一些踩坑的資料

  • 如果你使用的是umi2 + qiankun,那么請看這里
  • 另外你可能會踩到這些坑
  • 在其他技術(shù)棧中,你必須注意你的入口文件生命周期函數(shù)的導(dǎo)出和webpack出口的相關(guān)配置。

當(dāng)然了,如果你使用的是umi3,那么你可能不會踩到上面的坑,直接創(chuàng)建項目吧

我們使用umi3創(chuàng)建的項目如下:

  • app-main(主應(yīng)用,啟動端口8000)
  • app-one(子應(yīng)用1,啟動端口7001)
  • app-two(子應(yīng)用2,啟動端口9002)

項目搭建好了,那就簡單寫點業(yè)務(wù)吧,下面是我們假設(shè)的一個需求如下,在某租房公司業(yè)務(wù)上,有一個后臺。現(xiàn)在“房源模塊”和“合同模塊”分別由兩個團隊維護,于是,我們的技術(shù)方案如下:
主應(yīng)用提供容器,兩個子應(yīng)用一個負責(zé)房源信息,一個負責(zé)合同信息。
經(jīng)過開發(fā)的不懈努力,我們的各自模塊如下。



雖然頁面略顯簡陋,但有幾個重點很突出

  • 每一個項目都可以單獨啟動
  • 各自有各自的啟動端口
創(chuàng)建主應(yīng)用,將子應(yīng)用裝載進去

既然主應(yīng)用是一個盒子,要裝載兩個或者多個子應(yīng)用,那么配置主應(yīng)用將比較重要,這里我們不再贅述詳細過程,大家可以參照官網(wǎng)做更詳細的了解。這里我為了布局方便,使用的是ant design pro的模板,當(dāng)然,你可以使用任何你想使用的layout,但是如果你使用的是umi,那么請安裝插件plugin-qiankun。
這里,我們也是參照參照官網(wǎng),對主應(yīng)用做了配置。

  1. 注冊子應(yīng)用
  qiankun: {
    master: {
      apps: [
        {
          name: 'app-one', // 唯一 id
          entry: '//localhost:7001',
        },
        {
          name: 'app-two', // 唯一 id
          entry: '//localhost:9001', // html entry
        },
      ],
    },
  },
  1. 裝載子應(yīng)用

我們采用的是組件方式注冊的子組件,拿‘a(chǎn)pp-one’為例

import { MicroApp } from 'umi';

const MyPage = () => {
  return (
    <div>
      <div>
       <MicroApp
        name="app-one"
        autoSetLoading
        className="myContainer"
        wrapperClassName="myWrapper"
      />
      </div>
    </div>
  )
}
export default MyPage

實際上,在你注冊子應(yīng)用,裝載子應(yīng)用的這個過程中,和我們react組件的路由注冊和組件創(chuàng)建非常的相似,大家可以類比著去理解。
之所以用組件式,是因為這樣,我們就可以用ant design pro的路由和菜單耦合的功能,如果你使用的其他layout做主應(yīng)用,那么你可以使用組件形式,也可以使用路由形式,這里官網(wǎng)描述的都很清楚,就不一一贅述了。我使用的是組件形式,那么我必須在路由中注冊這個頁面組件

{
  path: 'app-one',
  name: 'houseManage',
  component: './appOne'
},
{
  path: 'app-two',
  name: 'hetongManage',
  component: './appTwo',
  exact: false, // 需要設(shè)置為fasle,這樣才能匹配到子應(yīng)用的下的子路由
},
  1. 子應(yīng)用配置

一個微前端子應(yīng)用,是需要做相應(yīng)的配置的,不然不會自然的集成進主應(yīng)用中。
你大概需要注意的是

  • package.json中必須有一個name的字段,來保證子應(yīng)用的唯一性
  • 如果你使用的是umi3,那么你需要這樣開啟即可
  qiankun: {
    slave: {},
  },

當(dāng)然了,在這之前,你需要安裝qiankun插件,才能在umi中配置這一項

yarn add @umijs/plugin-qiankun -D
  • 如果你使用的技術(shù)棧(上文說過了,這再贅述一下),那么你還另外需要注意其入口文件的生命周期函數(shù)處理和webpack關(guān)于output的配置,那你就必須要按照文檔的步驟來集成了。
啟動

經(jīng)過上面的一系列操作,我們的主應(yīng)用已經(jīng)可以訪問裝載的子應(yīng)用了。


小結(jié)

上面的一些列操作,都是微前端中最基礎(chǔ)的用法,如果你耐心看文檔,多踩踩坑,基本毫無壓力的就搭建出了上面的環(huán)境,當(dāng)然了,如果你已經(jīng)對搭建一個微前端環(huán)境很熟悉,你就會覺得,我上面說了一頓“廢話”,然后你就可以自動的跳過了。下面我們來探究,子應(yīng)用之間的融合。

子應(yīng)用之間的融合

通過上面的demo,我們已經(jīng)了解到了一些初步的概念,按照我們上面demo的實現(xiàn),就是說一個url(路由)對應(yīng)一個子應(yīng)用,然后這樣,我們就可以把一個大的應(yīng)用拆分成不同的小塊。

路由與應(yīng)用綁定的方式簡單直觀,是微前端中最為常見的使用場景,通常我們會用這種方式將一堆獨立域名訪問的 MPA 應(yīng)用,整合成一個一體化的 SPA 應(yīng)用。

下面我們來思考一個問題:
我們能不能在一個Url下掛載多個子應(yīng)用?
如果按照上面我們demo中的樣子,那必然是不行的,因為url的唯一性,我們只能在一個url下訪問一個唯一確定的資源,但是我們能不能解決這個問題呢?

路由模式(小插曲)

因為我們的主應(yīng)用是Spa,那么Url也就對應(yīng)了路由,下面做個小插曲,來探索一下,不同模式的路由,對于url的影響是什么,看看有沒有什么空子可以讓我們鉆一下,從而解決上面我們提出的問題。
眾所周知,Spa中的路由模式有兩種:browserhash。其中各自的原理簡單描述如下

  • 對于hash: 通過監(jiān)聽Url的location.hash,觸發(fā)onhashchange然后拿對應(yīng)的前端資源,這里會依賴Url
  • 對于browser:使用HTML5的history對象中的pushState()replaceState()這兩個api來實現(xiàn)的跳轉(zhuǎn),加載對應(yīng)的資源。這里也會依賴Url變化。

那么,我們有沒有一種方式,可以擺脫Url的控制來加載對應(yīng)的前端資源呢?如果可以,那么我們是不是就可以在同一個Url下加載多個微前端的子應(yīng)用了呢?
實際上,路由還有另外一個不為人知的類型,那就是memory類型,這里我對memory路由類型做一個簡單的描述。

  • memory類型:

不會把地址存放在URL上,而是將地址存放在本地或者數(shù)據(jù)庫中,在使用時獲取本地或者數(shù)據(jù)庫中的地址,然后匹配相對應(yīng)的資源。

看上面的描述可能有點懵逼,看一下代碼:

//例如
//獲取保存在localStorage中的地址
const path = window.localStorage.getItem('path');

//當(dāng)用戶在某個頁面就重新設(shè)置localStorage中的地址
//userHref是用戶所在的頁面
window.localStorage.setItem('path', userHref);

也就是說,我們可以把頁面存在本地或者其他地方,而脫離Url唯一性的束縛。因為,這個路由模式比較適用于“非瀏覽器”的場景,故這個路由模式也變得鮮為人知,但是他卻能很好的解決我們上面提出的問題。
當(dāng)然,也就是官網(wǎng)中說的

除了導(dǎo)航應(yīng)用之外,App1 與 App2 均依賴瀏覽器 url,為了讓 App1 嵌套 App2,兩個應(yīng)用同時存在,我們需要在運行時將 App2 的路由改為 memory 類型。

做一個子應(yīng)用嵌套的demo

umi為我們提供了MicroAppWithMemoHistory組件,我們可以直接使用,這個組件所引用的頁面或者應(yīng)用,使用的就是memory模式 ?,F(xiàn)在我們根據(jù)一個假設(shè)的需求,搭建一個嵌套的demo。
“在現(xiàn)有的項目中,我們在房源信息頁面中訪問合同列表和合同詳情”。
于是我們就可以這樣去改造我們的項目了。
在app-one“房源信息”應(yīng)用中,我們需要需要這樣修改他的配置項

  qiankun: {
    slave: {},
    master: {
      // 注冊子應(yīng)用信息
      apps: [
        {
          name: 'app-two', // 唯一 id
          entry: '//localhost:9001', // html entry
        },
      ],
    },
  },

這樣,就像我們在主應(yīng)用中注冊子應(yīng)用一樣,我們在子應(yīng)用之間也是可以這樣注冊的,不同的是,我們在裝載的時候,就不能想在主應(yīng)用中裝載那樣了,這時候就是我們用MicroAppWithMemoHistory的時候。我們這樣改造了我們的"房源信息"列表

...
    <>
      <h1>房源信息</h1>
      <Button onClick={btnClick} type="primary">查看合同列表</Button>
      <Table columns={columns} dataSource={data} />
      <Modal
        title="合同列表"
        visible={visible}
        width="100%"
        onOk={() => setVisible(false)}
      >
// 我們可以在url出書寫子應(yīng)用中的路由,而對應(yīng)加載模塊
        <MicroAppWithMemoHistory name="app-two" url="/"/>
      </Modal>
    </>

通過這樣的操作后,我們就可以在/app-one/這一個Url下同時加載兩個或者多個子應(yīng)用了。


當(dāng)然,我們也可以不讓其出現(xiàn)在彈窗中,設(shè)想,我們有一個中臺的分析頁面,這個頁面是由多個項目組成的,那么也就可以使用上面的場景了,我們還用我們的demo為例


可以看到,我們只需要簡單的代碼,就可以實現(xiàn)子應(yīng)用之間的嵌套了

寫在最后

關(guān)于qiankun微前端的搭建,社區(qū)的知識已經(jīng)相當(dāng)泛濫,實際上,我們面向官網(wǎng)就可以解決大部分搭建過程中遇到的坑。這篇文章的目的在于,介紹微應(yīng)用之間的融合,讓大家認識到這種開發(fā)模式。同時,知道鮮為人知的‘memory’路由模式。當(dāng)然了,微應(yīng)用之間也是可以像react組件那樣做通信的,這也是比較基礎(chǔ)的知識了,我想社區(qū)說的比我更完善吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 夜鶯2517閱讀 128,174評論 1 9
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,855評論 28 54
  • 兔子雖然是枚小碩 但學(xué)校的碩士四人寢不夠 就被分到了博士樓里 兩人一間 在學(xué)校的最西邊 靠山 兔子的室友身體不好 ...
    待業(yè)的兔子閱讀 2,774評論 2 9
  • 信任包括信任自己和信任他人 很多時候,很多事情,失敗、遺憾、錯過,源于不自信,不信任他人 覺得自己做不成,別人做不...
    吳氵晃閱讀 6,375評論 4 8

友情鏈接更多精彩內(nèi)容