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

          【微前端】1228- Vite 微前端實踐,實現(xiàn)一個組件化的方案

          共 7380字,需瀏覽 15分鐘

           ·

          2022-02-13 01:30

          什么是微前端

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

          微前端借鑒了微服務(wù)的架構(gòu)理念,將一個龐大的前端應(yīng)用拆分為多個獨立靈活的小型應(yīng)用,每個應(yīng)用都可以獨立開發(fā)、獨立運行、獨立部署,再將這些小型應(yīng)用聯(lián)合為一個完整的應(yīng)用。微前端既可以將多個項目融合為一,又可以減少項目之間的耦合,提升項目擴展性,相比一整塊的前端倉庫,微前端架構(gòu)下的前端倉庫傾向于更小更靈活。

          特性

          • 技術(shù)棧無關(guān) 主框架不限制接入應(yīng)用的技術(shù)棧,子應(yīng)用可自主選擇技術(shù)棧
          • 獨立開發(fā)/部署 各個團隊之間倉庫獨立,單獨部署,互不依賴
          • 增量升級 當(dāng)一個應(yīng)用龐大之后,技術(shù)升級或重構(gòu)相當(dāng)麻煩,而微應(yīng)用具備漸進式升級的特性
          • 獨立運行時 微應(yīng)用之間運行時互不依賴,有獨立的狀態(tài)管理
          • 提升效率 應(yīng)用越龐大,越難以維護,協(xié)作效率越低下。微應(yīng)用可以很好拆分,提升效率

          目前可用的微前端方案

          微前端的方案目前有以下幾種類型:

          基于 iframe 完全隔離的方案

          作為前端開發(fā),我們對 iframe 已經(jīng)非常熟悉了,在一個應(yīng)用中可以獨立運行另一個應(yīng)用。它具有顯著的優(yōu)點:

          1. 非常簡單,無需任何改造
          2. 完美隔離,JS、CSS 都是獨立的運行環(huán)境
          3. 不限制使用,頁面上可以放多個 iframe 來組合業(yè)務(wù)

          當(dāng)然,缺點也非常突出:

          1. 無法保持路由狀態(tài),刷新后路由狀態(tài)就丟失
          2. 完全的隔離導(dǎo)致與子應(yīng)用的交互變得極其困難
          3. iframe 中的彈窗無法突破其本身
          4. 整個應(yīng)用全量資源加載,加載太慢

          這些顯著的缺點也催生了其他方案的產(chǎn)生。

          基于 single-spa 路由劫持方案

          single-spa 通過劫持路由的方式來做子應(yīng)用之間的切換,但接入方式需要融合自身的路由,有一定的局限性。

          qiankun 孵化自螞蟻金融科技基于微前端架構(gòu)的云產(chǎn)品統(tǒng)一接入平臺。它對 single-spa 做了一層封裝。主要解決了 single-spa 的一些痛點和不足。通過 import-html-entry 包解析 HTML 獲取資源路徑,然后對資源進行解析、加載。

          通過對執(zhí)行環(huán)境的修改,它實現(xiàn)了 JS 沙箱樣式隔離 等特性。

          京東 micro-app 方案

          京東 micro-app 并沒有沿襲 single-spa 的思路,而是借鑒了 WebComponent 的思想,通過 CustomElement 結(jié)合自定義的 ShadowDom,將微前端封裝成一個類 webComponents 組件,從而實現(xiàn)微前端的組件化渲染。

          Vite 上使用微前端

          我們從 我們從 UmiJS 遷移到了 Vite 之后,微前端也成為了勢在必行,當(dāng)時也調(diào)研了很多方案。

          為什么沒用 qiankun

          qiankun 是目前是社區(qū)主流微前端方案。它雖然很完善、流行,但最大的問題就是不支持 Vite。它基于 import-html-entry 解析 HTML 來獲取資源,由于 qiankun 是通過 eval 來執(zhí)行這些 js 的內(nèi)容,而 Vite 中的 script 標(biāo)簽類型是 type="module",里面包含 import/export 等模塊代碼, 所以會報錯:不允許在非 type="module"script 里面使用 import

          退一步實現(xiàn),我們采用了 single-spa 的方式,并使用 systemjs 的方式進行了微前端加載方案,也踩了不少的坑。single-spa 沒有一個友好的教程來接入,文檔雖然多,但大多都在講概念,當(dāng)時讓人覺得有一種深奧的感覺。

          后來看了它的源碼發(fā)現(xiàn),這都是些什么……里面大部分代碼都是圍繞路由劫持而展開的,根本沒有文檔上那種高大上的感覺。而我們又用不到它路由劫持的功能,那我們?yōu)槭裁匆盟?/p>

          從組件化的層面來說 single-spa 這種方式實現(xiàn)得一點都不優(yōu)雅。

          1. 它劫持了路由,與 react-router 和組件化的思維格格不入
          2. 接入方式一大堆繁雜的配置
          3. 單實例的方案,即同一時刻,只有一個子應(yīng)用被展示

          后來琢磨著 single-spa 的缺點,我們可以自己實現(xiàn)一個組件化的微前端方案。

          如何實現(xiàn)一個簡單、透明、組件化的方案

          通過組件化思維實現(xiàn)一個微應(yīng)用非常簡單:子應(yīng)用導(dǎo)出一個方法,主應(yīng)用加載子應(yīng)用并調(diào)用該方法,并傳入一個 Element 節(jié)點參數(shù),子應(yīng)用得到該 Element 節(jié)點,將本身的組件 appendChildElement 節(jié)點上。


          類型約定

          在此之前我們需要約定一個主應(yīng)用與子應(yīng)用之間的一個交互方式。主要通過三個鉤子來保證應(yīng)用的正確執(zhí)行、更新、和卸載。

          類型定義:

          export interface AppConfig {
          // 掛載
          mount?: (props: unknown) => void;
          // 更新
          render?: (props: unknown) => ReactNode | void;
          // 卸載
          unmount?: () => void;
          }

          子應(yīng)用導(dǎo)出

          通過類型的約定,我們可以將子應(yīng)用導(dǎo)出:mountrenderunmount 為主要鉤子。

          React 子應(yīng)用實現(xiàn):

          export default (container: HTMLElement) => {
          let handleRender: (props: AppProps) => void;

          // 包裹一個新的組件,用作更新處理
          function Main(props: AppProps) {
          const [state, setState] = React.useState(props);
          // 將 setState 方法提取給 render 函數(shù)調(diào)用,保持父子應(yīng)用觸發(fā)更新
          handleRender = setState;
          return <App {...state} />;
          }

          return {
          mount(props: AppProps) {
          ReactDOM.render(<Main {...props} />, container);
          },
          render(props: AppProps) {
          handleRender?.(props);
          },
          unmount() {
          ReactDOM.unmountComponentAtNode(container);
          },
          };
          };

          Vue 子應(yīng)用實現(xiàn):

          import { createApp } from 'vue';
          import App from './App.vue';

          export default (container: HTMLElement) => {
          // 創(chuàng)建
          const app = createApp(App);
          return {
          mount() {
          // 裝載
          app.mount(container);
          },
          unmount() {
          // 卸載
          app.unmount();
          },
          };
          };

          主應(yīng)用實現(xiàn)

          React 實現(xiàn)

          其核心代碼僅十余行,主要處理與子應(yīng)用交互 (為了易讀性,隱藏了錯誤處理代碼)

          export function MicroApp({ entry, ...props }: MicroAppProps) {
          // 傳遞給子應(yīng)用的節(jié)點
          const containerRef = useRef(null);
          // 子應(yīng)用配置
          const configRef = useRef();

          useLayoutEffect(() => {
          import(/* @vite-ignore */ entry).then((res) => {
          // 將 div 傳給子應(yīng)用渲染
          const config = res.default(containerRef.current);
          // 調(diào)用子應(yīng)用的裝載方法
          config.mount?.(props);
          configRef.current = config;
          });
          return () => {
          // 調(diào)用子應(yīng)用的卸載方法
          configRef.current?.unmount?.();
          configRef.current = undefined;
          };
          }, [entry]);

          return <div ref={containerRef}>{configRef.current?.render?.(props)}div>;
          }

          完成,現(xiàn)在已經(jīng)實現(xiàn)了主應(yīng)用與子應(yīng)用的裝載、更新、卸載的操作。現(xiàn)在,它是一個組件,可以同時渲染出多個不同的子應(yīng)用,這點就比 single-spa 優(yōu)雅很多。

          entry 子應(yīng)用地址,當(dāng)然真實情況會根據(jù) devprod 模式給出不同的地址:

          "micro-app" entry="http://localhost:3002/src/main.tsx" />

          Vue 實現(xiàn)

          
          
          <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>
                    青青草污视频 | 国产777在线 | 在线做爱| 在线手机av| 91蜜桃婷婷狠狠久久综合 |