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

          Vite 微前端實(shí)踐,實(shí)現(xiàn)一個(gè)組件化的方案

          共 7185字,需瀏覽 15分鐘

           ·

          2022-02-27 05:49

          什么是微前端

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

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

          特性

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

          目前可用的微前端方案

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

          基于 iframe 完全隔離的方案

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

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

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

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

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

          基于 single-spa 路由劫持方案

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

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

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

          京東 micro-app 方案

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

          Vite 上使用微前端

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

          為什么沒(méi)用 qiankun

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

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

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

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

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

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

          如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單、透明、組件化的方案

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


          類型約定

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

          類型定義:

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

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

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

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

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

          // 包裹一個(gè)新的組件,用作更新處理
          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)用實(shí)現(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)用實(shí)現(xiàn)

          React 實(shí)現(xiàn)

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

          export function MicroApp({ entry, ...props }: MicroAppProps) {
          // 傳遞給子應(yīng)用的節(jié)點(diǎn)
          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)實(shí)現(xiàn)了主應(yīng)用與子應(yīng)用的裝載、更新、卸載的操作?,F(xiàn)在,它是一個(gè)組件,可以同時(shí)渲染出多個(gè)不同的子應(yīng)用,這點(diǎn)就比 single-spa 優(yōu)雅很多。

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

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

          Vue 實(shí)現(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>
                    不卡高清无码在线 | WWW,色老板,C0m | 天堂资源在线观看 | 影音先锋麻豆 | 中文字幕视频在线观看 |