探索微前端的場景極限

本文主要介紹總結了一些基於 qiankun 的微前端應用場景與實踐

基礎場景

與路由綁定的方式渲染微應用

一般狀況下,咱們接觸的最多的微前端的實踐,是以 URL/路由 爲維度來劃分咱們的微應用,以 OneX 平臺(螞蟻金融雲基於微前端架構打造的統一接入平臺)爲例:html

image.png

接入這類平臺的微應用,一般只須要提供本身的 entry html 地址,併爲其分配一個路由規則便可。前端

這背後的實現則是基於 qiankun 的 registerMicroApps API,如:react

import { registerMicroApps } from 'qiankun';

registerMicroApps([
  {
    name: 'app1', 
    container: '#container', 
    entry: '//micro-app.alipay.com/', 
    activeRule: '/app1' 
  }
])

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

但這類場景也有本身的侷限性:github

  1. 因爲URL/路由的 惟一性/排他性 的特色,這種方式只適用單實例場景需求
  2. 微應用的調度都是由路由系統來自動處理的,雖然省事可是碰到更復雜的需求,如同一個路由下,根據不一樣的用戶權限展現不一樣的微應用這類個性化訴求,須要寫一些中間層代碼來曲線救國
  3. 應用掛載的容器節點等需提早準備好,否則碰到 動態/嵌套 路由這類狀況,可能會由於路由 listener 監聽觸發的時序不肯定,致使微應用沒法完成掛載的問題

以組件的方式使用微應用

qiankun 2.0 的發佈帶來一個全新的 API loadMicroApp,經過這個 API 咱們能夠本身去控制一個微應用加載/卸載,這個方式也是 qiankun 2.0 的重磅特性:npm

import { loadMicroApp } from 'qiankun';

// do something

const container = document.createElement('div');
const microApp = loadMicroApp({ name: 'app', container, entry: '//micro-app.alipay.com' });

// do something and then unmount app
microApp.unmout();

// do something and then remount app
microApp.mount();

開發者能夠在脫離路由的限制下,以更自由的方式去渲染咱們的微應用。基於 loadMicroApp API,咱們只須要作一些簡單的封裝,便可以相似組件的開發體驗,完成微應用的接入,以 React 爲例:api

第一步:封裝一個 MicroApp 組件:瀏覽器

import { loadMicroApp } from 'qiankun';
import React from 'react';

export default class MicroApp extends React.Component {

  containerRef = React.createRef();
  microApp = null;

  componentDidMount() {
    const { name, entry, ...props } = this.props;
    this.microApp = loadMicroApp(
      { name, entry, container: this.containerRef.current, props },
    );
  }

  componentWillUnmount() {
    this.microApp.unmount();
  }

  componentDidUpdate() {
    const { name, entry, ...props } = this.props;
    this.microApp.update(props);
  }

  render() {
    return <div ref={this.containerRef}></div>;
  }
}

第二步:經過 MicroApp 組件引入微應用:微信

import MicroApp from './MicroApp';
import React from 'react';

class App extends React.Component {
    render() {
    return (
      {
        this.props.admin
          ? <MicroApp name="admin" entry="//localhost:8080/" level={10} />
          : <MicroApp name="guest" entry="//localhost:8081/" level={1} />
      }
    )
  }
}

若是你是 umi 應用,那隻須要直接使用插件封裝好的組件便可:babel

import { MicroApp } from 'umi';

function MyComponent() {
  return (
    <div>
      <MicroApp name="qiankun" age={1.5} stars={8700} />
    </div>
  )
}

這類方式適用於一些可共用的、帶業務邏輯的服務型組件(相似於咱們之前常說的端對端組件):好比帶聊天交互的客服機器人、帶引導功能的 intro 服務等。

如螞蟻 sofa 產品控制檯中的這個使用入門微應用:

image

右側呼出的窗口即爲一個獨立開發、獨立發佈的微應用

經過組件的這種方式,咱們能夠徹底自主的控制微應用的渲染,並與之作一些複雜的交互。不管是在開發者的編碼心智,仍是用戶的體驗上,都跟使用一個普通的業務組件無異。

組件的方式很是靈活,幾乎解決了全部路由綁定方式渲染微應用的問題,但也有本身的一些侷限:好比咱們會要求這類微應用必須是不帶路由系統的 widget 類型,否則也會出現多實例時路由衝突的問題。

嵌套渲染場景

有一些更復雜的場景中,咱們可能須要使用到「套娃」的方式集成咱們的若干微應用。

好比咱們要在應用 A 下集成應用 B 的商品列表頁,而後在應用 B 的商品列表頁呼出應用 C 的買家詳情頁,全部應用的喚起都以 彈層/抽屜 這種不刷新的交互來完成。

在有了上面兩個場景的實踐經驗後,咱們很容易得出這樣的組合邏輯:

在應用 A 中經過調用 loadMicroApp(B) 的方式喚起微應用 B,而後在微應用 B 中經過 loadMicroApp(C) 的方式喚起微應用 C,經過這樣的調用鏈路便可很完美的完成產品上的訴求。

可是現實狀況每每沒有那麼簡單,前面提到過,若想要 loadMicroApp API 能符合預期的運行,咱們須要確保被加載的微應用是不含本身的路由系統,不然會出現多個應用間路由系統互相 搶佔/衝突 的狀況。

而現實狀況是,咱們大部分須要複用的頁面/組件,都會是某個站點的局部路由頁,不多有人會專門起一個倉庫,用來專門把這個頁面抽取成一個微應用,好比上面提到的買家詳情頁。

這種場景下,咱們其實只須要確保微應用的路由系統不會干擾到全局的 URL 系統便可。幸運的是 react-router 的 memory history 模式很好的解決了這一問題。若是你是一個 umi 應用,只須要直接使用咱們封裝好的組件便可完成 memory history 的運行時切換:

import { MicroAppWithMemoHistory } from 'umi';

<Drawer>
  <MicroAppWithMemoHistory name="buyer" url='/buyers/123' />
</Drawer>

交互效果能夠參考:

image

圖中 app1/app2 均是子應用,可是在 app1 中能夠再經過抽屜呼出 app2,同時瀏覽器地址欄也不會被 app2 的路由干擾。

關於嵌套渲染相關的詳細介紹,能夠看這篇《基於微前端的大型中臺項目融合方案》

極限渲染場景

若是你以爲嵌套微應用就是咱們場景的天花板了,那未免有點小看羣衆們的想象力了。

在咱們內部的一個設計工程化平臺裏,咱們經過 qiankun 成功的把一個微應用的 20+ 路由頁同時渲染到了一個 url 下:

image.png

上圖中右側列表中每個 demo 都是經過 qiankun 渲染的一個獨立微應用實例,而這裏面每個微應用實例,實際對應是同一個 react 應用的不一樣路由頁。

不一樣於前面幾個場景,將同一個應用的不一樣頁面,同時渲染到主應用的不一樣 UI 容器中這個需求下,有幾個比較特殊的問題須要去考慮:

  1. 是否須要特殊的微應用生產方式
  2. 多路由系統共存帶來的 衝突/搶佔 問題
  3. 不一樣微應用間的樣式隔離
  4. 如何優化渲染性能:既然每個微應用實例實際對應的是同一個應用,那咱們如何儘量多的複用一些運行時或者沙箱,從而下降這麼多微應用同時渲染代理的運行時開銷

篇幅考慮,針對這類場景優化的技術細節不在這裏介紹了,後面會單獨寫一篇來介紹

解決了這些問題後,咱們只須要在咱們的項目中這麼去使用就能夠了:

// MicroAppWithMemoHistory 是基於 memory history 封裝的微應用加載器
import { MicroAppWithMemoHistory } from 'umi';

function Home {
  return (
    <div>
      { /* 將同一個應用的不一樣路由頁同時渲染出來 */ }
      <MicroAppWithMemoHistory name="demo" url="/demo1" />
      <MicroAppWithMemoHistory name="demo" url="/demo2" />
      <MicroAppWithMemoHistory name="demo" url="/demo3" />
      <MicroAppWithMemoHistory name="demo" url="/demo4" />
      <MicroAppWithMemoHistory name="demo" url="/demo5" />
      <MicroAppWithMemoHistory name="demo" url="/demo6" />
    </div>
  )
}

更多的想象空間

工程上的想象空間

微前端架構除了其帶來的巨石應用解構、技術棧無關等工程能力外,也爲咱們對一些已有的工程問題帶來了新的解題思路,好比:

npm 包分發業務組件背後的工程問題

在之前,咱們常常經過發佈 npm 包的方式複用/共享咱們的業務組件,但這種方式存在幾個明顯的問題:

  1. npm 包的更新下發須要依賴產品從新部署纔會生效
  2. 時間一長就容易出現依賴產品版本割裂致使的體驗不一致
  3. 沒法灰度
  4. 技術棧耦合

說白了就是 npm 包這種靜態的共享方式,喪失了動態下發代碼的能力,致使了其過慢的工程響應速度,這在如今雲服務流行的時代就會顯得格外扎眼。而微前端這種純動態的服務依賴方式,剛好能方便的解決上面的問題:被依賴的微應用更新後的產物,在產品刷新後便可動態的獲取到,若是咱們在微應用加載器中再輔以灰度邏輯,那麼動態更新帶來的變動風險也能獲得有效的控制。

新的 UI 共享模式

在之前,若是咱們但願複用一個站點的局部 UI,幾乎只有這樣一條路徑:從業務系統中抽取出一個組件 -> 發佈 npm 包 -> 調用方使用 npm 包。

且不說前面提到的 npm 自身的問題,單單是從一個已有的業務系統中抽取出一個 UI 組件這個事情,均可可以咱們喝一壺的了。咱們不只要在物理層面,將這部分代碼抽取成若干個單獨的文件,同時還要考慮如何跟已有的系統作上下文解耦,這類工做出如今一個越是年代久遠的項目上,實施起來就越是困難,作過這類事情的同窗應該都會深有體會。

不一樣於組件庫的研發流程,微前端的場景下,大部分時候咱們不會爲了去複用一個 UI,而去專門寫一個微應用出來。一般咱們指望的是,從一個已有系統中,直接選取咱們須要複用的部分,嵌入到咱們本身的容器裏進行渲染。

基於上面提到過的微應用多實例的渲染方案,咱們能夠考慮將須要複用的組件,以路由 URL 做爲 ID 的方式導出。好比咱們有這樣一個 A 應用有一個這樣的頁面:

function OnePage() {
  return (
    <div>
      <SearchForm/>
      <UserList/>
    </div>
  )
}

咱們有另一個應用,但願單獨複用 A 應用的用戶列表部分的交互跟 UI,那咱們只須要多加一條路由規則:

<Switch>
  ...
  <Route path="/userList" memory={true}>
    <UserList/>
  </Route>
</Switch>

依賴方只須要配合 memory history 並指定 url 爲 /userList 便可完成渲染(參考上面嵌套渲染章節)。

站點即配置

當咱們將全部能夠共享的服務單元變成一個個獨立的微應用以後,咱們即可以經過配置的方式描述咱們的站點了,相似:

{
  layout: 'admin-pro',
  apps: [
    { name: 'abc', props: {} },
    { name: 'bcd', props: {} },
    { name: 'cde', props: {} },
  ]
}

其中 layout 能夠是一個帶基礎交互框架、用戶鑑權等公共能力的微應用,也能夠是一組微應用的集合(相似 babel 中的 preset 插件),而 apps 則是一組須要在當前站點渲染出來的業務微應用。

這種方式很是適用於,當咱們要在一個業務域下開發多個細分服務站點時,經過這種配置的方式,配合一個運行時的微應用編排引擎,快速的生成一系列視覺一致、交互統一的業務站點出來。

產品上的想象空間

在有了上面那些場景背後的技術支撐後,在產品上,咱們就已經多出不少想象空間了。

但咱們還能夠想的更極限一點:

好比咱們知道微信有一個公衆號浮窗的功能,包括安卓系統常見的小窗模式,解決的就是空間獨佔、以及跨空間時的交互問題。

image.png

那咱們在中後臺也能夠參考相似的設計,將不一樣空間的關聯性操做以這種非獨佔的形態聚合到一塊兒,從而下降流程上的斷層感,提高產品體驗。

demo.jpg

(這裏本有一張螞蟻內部系統的交互示意圖,因涉及保密信息沒法公開,有興趣的同窗能夠選擇加入咱們後作進一步的交流分享😆)

寫在最後

微前端提供的這些漸進式更新、動態組合、 服務拓展的能力,相信你們經過咱們介紹的這些常見場景,以及一些極致條件下的解決方案中能窺其一二。

但須要強調的是,任何技術架構都不多是銀彈,咱們沒必要對微前端過於神化/劣化。本篇僅但願在分享了咱們在微前端領域的一系列探索以後,能給其餘開發者帶來一些新的選擇和啓發,從而爲其工程及產品上帶來更多的可能性。

最後的最後,誠招天下英雄

上面提到的這些案例,包括一些想象中的場景,正在咱們團隊內有條不紊地發生着,只是時機未到還不能更詳細的對外宣傳並與你們分享,歡迎有興趣的同窗能夠加入咱們,與咱們一塊兒去探索更多微前端的可能性。

若是您對微前端感興趣,請發簡歷到 youzhi.lk@antfin.com,咱們很是指望有機會能與您共事,探索出微前端下更多的場景可能性。

若是您對微前端沒什麼興趣,沒關係,只須要您對 antd、AntV、dva、umi、eggjs、ahooks 中任一類別的領域感興趣,您也能夠發簡歷到 youzhi.lk@antfin.com 來與咱們共商大事。

團隊介紹

  • 大部門:螞蟻集團體驗技術部
  • 部門主管:玉伯
  • 部門開源做品:antd、AntV、dva、umi、eggjs、qiankun、ahooks 等
  • 部門商業化產品:語雀
  • 直屬部門:平臺前端技術部 - 部門主管:偏右(antd 負責人),部門介紹:https://www.yuque.com/afx/abo...
  • 要求:基本沒要求,p5~p8 咱們都須要,只要您想來試試就能夠投簡歷到 youzhi.lk@antfin.com,HC 很是多!!
  • base 地:杭州、上海、成都 三地任選
相關文章
相關標籤/搜索