<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>

          [譯]一種基于模塊聯(lián)邦的插件前端

          共 4998字,需瀏覽 10分鐘

           ·

          2024-04-10 19:00

          2a4e2f4922d660567fe6e5c605284ab4.webp

          原文:https://malcolmkee.com/blog/a-plugin-based-frontend-with-module-federation/

          在談及模塊聯(lián)邦及其獨立構(gòu)建和部署的特性(通常稱為微前端)時,一個常見的問題是,“為什么這比使用iframe更好?”雖然這的確是一個問題,特別是當只使用模塊聯(lián)邦拼接多個UI時,其好處可能不會立即顯現(xiàn)的時候;答案就在于它無縫集成多個前端應(yīng)用程序,并允許組件和函數(shù)調(diào)用一起工作的能力。這就是為什么模塊聯(lián)邦是目前構(gòu)建微前端應(yīng)用程序的最佳技術(shù)。

          在本文中,我將為前端應(yīng)用程序提供一個利用模塊聯(lián)邦的插件架構(gòu)。該架構(gòu)允許開發(fā)人員在既有應(yīng)用程序中添加、刪除或更新功能,而無需對應(yīng)用程序進行任何更改。得益于模塊聯(lián)邦實現(xiàn)的無縫集成,該插件架構(gòu)才成為可能。

          插件架構(gòu)是什么?

          插件架構(gòu)(plugin architecture)是一種軟件架構(gòu),它允許 第三方開發(fā)者 通過編寫插件來擴展現(xiàn)有軟件的功能。

          在插件系統(tǒng)中,“core”軟件提供了 一組定義好的接口、API或鉤子,以使開發(fā)人員在不修改核心軟件的前提下添加新特性或修改應(yīng)用程序的行為。這種方法促進了模塊化,因為插件可以獨立于核心軟件開發(fā),并且可以被輕松添加或刪除以自定義應(yīng)用程序。

          插件系統(tǒng)通常用于需要大量定制的系統(tǒng)。例如,流行的軟件,如瀏覽器,文本編輯器,構(gòu)建工具和內(nèi)容管理系統(tǒng)(CMS)都使用插件系統(tǒng),使開發(fā)人員能夠向軟件添加新功能。VS Code 是一個流行的代碼編輯器,它的擴展市場就是一個插件系統(tǒng)的例子。類似地,流行的 CMS WordPress 使用插件系統(tǒng),使用戶能夠向其網(wǎng)站添加新功能。

          以模塊聯(lián)邦實現(xiàn)的插件系統(tǒng)

          模塊聯(lián)邦的一種典型模式包括一個單體應(yīng)用程序(host),它從多個較小的應(yīng)用程序(remote)中導入代碼。host和remote都可以獨立構(gòu)建和部署,并且可以使用模塊聯(lián)邦在運行時將它們縫合在一起。

          b002e7c46f9f77f90c6ebdea0469bc2c.webp

          將插件系統(tǒng)應(yīng)用于模塊聯(lián)邦,可以使host應(yīng)用程序或者說"core",在添加、更新或移除充當插件的remotes 時保持不變。唯一的約束是所有remote必須遵循一組定義好的接口或鉤子

          舉個例子,假設(shè)所有remote應(yīng)用都必須按照以下約定導出單個遠程模塊/register

                
                // src/register.tsx

          import { register } from '@company/core-plugin';
          import * as React from 'react';

          const OrdersPage = () => <h1>Orders</h1>;

          export default register({
          routes: [
          {
          path: 'orders',
          element: <OrdersPage />,
          },
          ],
          });

          來自包@company/core-pluginregister函數(shù)是一個身份函數(shù),用于強制類型安全:

                
                import { RouteObject } from 'react-router-dom';

          export interface Plugin {
            routes: Array<RouteObject>;
          }

          export const register = (plugin: Plugin) => plugin;

          通過所有remote都使用該接口暴露的register模塊,host就可以渲染已在所有remote上注冊的全部路由:

                
                //app.tsx in host

          import { Plugin } from '@company/core-plugin';
          import * as React from 'react';
          import { createBrowserRouter, RouterProvider } from 'react-router-dom';
          import { createRoot } from 'react-dom/client';

          const getAllRemotes = () =>
          Promise.all([
          import('microfrontend1/register'),
          import('microfrontend2/register'),
          import('microfrontend3/register'),
          ]) as Promise<Array<{ default: Plugin }>>;

          getAllRemotes()
          .then((mods) => mods.map((mod) => mod.default))
          .then((remotes) => {
          const router = createBrowserRouter(remotes.map((remote) => remote.routes).flat());

          createRoot(document.getElementById('app')!).render(<RouterProvider router={router} />);
          });

          如下例所示,每當在remote中增添新的路由,則host中無需改變單獨的代碼,只消在下次加載時便會自動出現(xiàn)了。

                
                // src/register.tsx

          import { register } from '@company/core-plugin';
          import * as React from 'react';

          const OrdersPage = () => <h1>Orders</h1>;
          const OrdersDetailsPage = () => <h1>Orders Details</h1>;

          export default register({
          routes: [
          {
          path: 'orders',
          element: <OrdersPage />,
          },
          {
          path: 'orders/:orderId',
          element: <OrdersDetailsPage />,
          },
          ],
          });

          可能的插件API

          在模塊聯(lián)邦中的插件架構(gòu)有了基本了解之后,你就可以通過創(chuàng)建更多的API或鉤子來提高host的可擴展性了。下面是一些支持常見用例的插件API。請記住,它們不是詳盡的,也不是必需的??梢愿鶕?jù)你的用例來決定其取舍,或者也可以創(chuàng)建自己的API。

          registerroutes 選項

          這個選項在前面的部分中討論過,是一個路由定義數(shù)組,通??梢詮哪闶褂玫穆酚善鲙熘袛U展(在我的例子中,我重用了react-router-dom中的RouteObject接口)。它還可以包括子導航,比如在你的應(yīng)用中要用tabs之類的時候。host將在構(gòu)造其路由之前合并來自所有remote的路由定義。

          從理論上講,多個remote的路由可能會相互沖突,例如使用'*'這類過度貪婪的路徑,當檢測到這種情況時,你應(yīng)該通過 linting 或控制臺錯誤消息來緩解。

          registernavItems 選項

          也就是一個導航項目列表;你的host應(yīng)用可能帶有導航,此屬性允許remote向其中添加/刪除項目。該屬性的可能定義為:

                
                interface NavItem {
            path: string;
            label: string;
            /** 用去嵌套導航的章節(jié)或者說組 */
            section: string;
            /** 排序用 */
            order: number;
            /** 圖標 */
            icon: React.ReactNode;
            /** 假設(shè)又區(qū)分了多種導航 */
            location: 'header' | 'footer' | 'sidebar';
          }

          結(jié)合了 <Slot /> 組件的 registerfills 選項

          如果需要將組件從一個remote嵌入到另一個remote,這兩個API可以幫上忙。

          想象一個客戶票證界面,它顯示多個部分,如客戶個人信息和過往訂單等。客戶票據(jù)界面由一個團隊維護,而客戶個人信息和訂單由另一個團隊開發(fā),每個團隊都維護著自己的remote應(yīng)用。

          1afec6439246c2fce9aea3107298b73e.webp

          要將客戶個人信息和過往訂單嵌入到客戶票證界面中,我們可以使用以下元素:

          1. 在客戶票證界面(在 customer-support-app 那個 remote 應(yīng)用里編寫)中,渲染一個<Slot id="customerTicketScreen" />組件。就其本身而言,它什么也沒有顯示。

          2. 在客戶個人數(shù)據(jù)和訂單兩個 remote 應(yīng)用中,為 register 提供 fills選項

                
                // src/register.tsx

          export default register({
          fills: [
          {
          slotId: 'customerTicketScreen', // 匹配在 support 中由 Slot 提供的 id
          component: PersonalInfoSection,
          },
          ],
          });
          1. 在 host 中,使用 React context 注入所有按 slotId 分組的 fills。在Slot組件中,讀取 context 的值,并按照slotIdid匹配,渲染所有 fills。

          usePluginEventEmitterusePluginEventListener

          讓來自多個 remote 的組件在同一個界面上共存,那么它們不可避免地要相互通信。usePluginEventEmitterusePluginEventListener 就是用于讓組件發(fā)出/監(jiān)聽事件的自定義鉤子。

          從原理上來講,這類鉤子可以使用 mitt 事件總線或 window.dispatch(CustomEvent) 這樣的自定義事件來實現(xiàn)。

          總結(jié)

          一個使用模塊聯(lián)邦的基于插件的前端架構(gòu),是創(chuàng)建復雜應(yīng)用程序的強大方法,這樣的應(yīng)用允許來自多個項目的UI組件無縫集成。通過使用插件系統(tǒng),開發(fā)人員可以在不修改host應(yīng)用的前提下擴展其功能。

          同時,雖然這種方法帶來諸多便利,留意其潛在的挑戰(zhàn)和走好鋼絲也是很重要的。例如,如果要在多應(yīng)用間復用工具函數(shù)或類,插件系統(tǒng)可能并不適用,反而 npm 包是個更好的選擇。盡管有這些潛在限制,經(jīng)過細心計劃和實現(xiàn),基于插件的前端架構(gòu)還是可以為構(gòu)建復雜應(yīng)用提供一個靈活和可擴展的平臺。





          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日韩综合 | 青娱乐最新官网 | 欧美五十路 | 欧美黄色性爱视频 | 一区二区三区成人电影 |