博客 / 詳情

返回

作為面試官,為什麼我推薦微前端作為前端面試的亮點?

點擊在線閲讀,體驗更好 鏈接
現代JavaScript高級小冊 鏈接
深入淺出Dart 鏈接
現代TypeScript高級小冊 鏈接
前段時間陸續面試了一波候選人,其中提到最多的就是微前端方案,微前端不像前端框架的面試題那樣,它更偏重於項目實戰,更加考察候選人的技術水平,不像React,Vue隨便一問,就是各種響應式原理,Fiber架構等等爛大街的。

為什麼選擇微前端作為項目亮點

如果你的簡歷平平無奇,面試官實在在你的簡歷上問不出什麼,那麼只能給你上點“手寫題”強度了

作為面試官,我經常聽到很多候選人説在公司做的項目很簡單,平常就是堆頁面,寫管理端,寫H5,沒有任何亮點,我以我一次面試候選人的經歷分享給大家

面試官:你為什麼選擇用微前端做管理端升級,你的項目很龐大麼?

候選人: 不是的,其實是我們把兩個管理端合併,讓用户方便使用。

面試官:咦,竟然這樣你們還不如用a標籤鏈接或者nginx轉發一下就行了,更加方便,殺雞焉用牛刀啊

候選人:為了讓客户體驗到單頁面的感覺,體驗感更好

面試官:enen....

從這裏你會覺得候選人的想法有點奇葩,但是換個角度來想,一定要等到項目龐大拆服務了才用微前端麼,我管理端項目一開始就上微前端不行麼。其實從這裏可以看出來,管理系統使用微前端的成本並不會太大,而且從後面的技術問答中,候選人的微前端還是挺優秀的,各個細節基本都涉略到了。

如果你在公司內部很閒,又是剛好負責無關緊要的運營管理端,那麼新的管理端可以一開始接入微前端方案,為未來的技術升級提供一個接口,風險也可控,還能夠倒騰技術,簡歷還能新增亮點,何樂而不為

另外提到H5了,就提多一嘴,H5面向C端用户比較多,這方面更應該關心一些性能指標數據,比如FPFCP等等,圍繞這些指標進行優化,亮點不就來了麼,這類例子比比皆是,要學會多挖掘

接下來是我作為面試官,經常考察候選人的問題,因為大部分候選人都是用qiankun框架,所以本文以qiankun框架為模板,重點剖析項目實戰中微前端中遇到的問題和原理

請解釋一下微前端的概念以及它的主要優點和挑戰?

f44b35de-1d21-45dd-9998-e65cc52c0266.png

微前端是一種將不同的前端應用組合到一起的架構模式。這些應用可以獨立開發、獨立部署、獨立運行,然後在一個主應用中進行集成。這種模式的主要目標是解決大型、長期演進的前端項目的複雜性問題。

主要優點:

  1. 解耦: 微前端架構可以將大型項目分解為多個可以獨立開發、測試和部署的小型應用。這種解耦可以提高開發效率,減少團隊間的協調成本。
  2. 技術棧無關: 不同的微前端應用可以使用不同的技術棧,這為使用新技術、升級舊技術提供了可能。
  3. 並行開發: 因為微前端應用是獨立的,所以多個團隊可以並行開發不同的應用,無需擔心相互影響。
  4. 獨立部署: 每個微前端應用可以獨立部署,這意味着可以更快地推出新功能,同時降低了部署失敗的風險。

主要挑戰:

  1. 性能問題: 如果不同的微前端應用使用了不同的庫或框架,可能會導致加載和運行的性能問題。
  2. 一致性: 保持不同的微前端應用在用户體驗、設計和行為上的一致性可能會比較困難。
  3. 狀態共享: 在微前端應用之間共享狀態可能會比較複雜,需要使用特殊的工具或模式。
  4. 複雜性: 儘管微前端可以解決大型項目的複雜性問題,但是它自身也帶來了一些複雜性,比如需要管理和協調多個獨立的應用。
  5. 安全性: 微前端架構可能會增加跨域等安全問題。

你能詳細描述一下 qiankun 微前端框架的工作原理嗎?

qiankun 是一個基於 single-spa 的微前端實現框架。它的工作原理主要涉及到以下幾個方面:
  1. 應用加載:qiankun 通過動態創建 script 標籤的方式加載子應用的入口文件。加載完成後,會執行子應用暴露出的生命週期函數。
  2. 生命週期管理:qiankun 要求每個子應用都需要暴露出 bootstrap、mount 和 unmount 三個生命週期函數。bootstrap 函數在應用加載時被調用,mount 函數在應用啓動時被調用,unmount 函數在應用卸載時被調用。
  3. 沙箱隔離:qiankun 通過 Proxy 對象創建了一個 JavaScript 沙箱,用於隔離子應用的全局變量,防止子應用之間的全局變量污染。
  4. 樣式隔離:qiankun 通過動態添加和移除樣式標籤的方式實現了樣式隔離。當子應用啓動時,會動態添加子應用的樣式標籤,當子應用卸載時,會移除子應用的樣式標籤。
  5. 通信機制:qiankun 提供了一個全局的通信機制,允許子應用之間進行通信。

在使用 qiankun 時,如果子應用是基於 jQuery 的多頁應用,你會如何處理靜態資源的加載問題?

在使用 qiankun 時,如果子應用是基於 jQuery 的多頁應用,靜態資源的加載問題可能會成為一個挑戰。這是因為在微前端環境中,子應用的靜態資源路徑可能需要進行特殊處理才能正確加載。這裏有幾種可能的解決方案:

方案一:使用公共路徑

在子應用的靜態資源路徑前添加公共路徑前綴。例如,如果子應用的靜態資源存放在 http://localhost:8080/static/,那麼可以在所有的靜態資源路徑前添加這個前綴。

方案二:劫持標籤插入函數

這個方案分為兩步:

    1. 對於 HTML 中已有的 img/audio/video 等標籤,qiankun 支持重寫 getTemplate 函數,可以將入口文件 index.html 中的靜態資源路徑替換掉。
    1. 對於動態插入的 img/audio/video 等標籤,劫持 appendChild、innerHTML、insertBefore 等事件,將資源的相對路徑替換成絕對路徑。

例如,我們可以傳遞一個 getTemplate 函數,將圖片的相對路徑轉為絕對路徑,它會在處理模板時使用:

start({
  getTemplate(tpl,...rest) {
    // 為了直接看到效果,所以寫死了,實際中需要用正則匹配
    return tpl.replace('<img src="./img/jQuery1.png">', '<img src="http://localhost:3333/img/jQuery1.png">');
  }
});

對於動態插入的標籤,劫持其插入 DOM 的函數,注入前綴。

beforeMount: app => {
   if(app.name === 'purehtml'){
       // jQuery 的 html 方法是一個挺複雜的函數,這裏只是為了看效果,簡寫了
       $.prototype.html = function(value){
          const str = value.replace('<img src="/img/jQuery2.png">', '<img src="http://localhost:3333/img/jQuery2.png">')
          this[0].innerHTML = str;
       }
   }
}

方案三:給 jQuery 項目加上 webpack 打包

這個方案的可行性不高,都是陳年老項目了,沒必要這樣折騰。

在使用 qiankun 時,如果子應用動態插入了一些標籤,你會如何處理?

在使用 qiankun 時,如果子應用動態插入了一些標籤,我們可以通過劫持 DOM 的一些方法來處理。例如,我們可以劫持 appendChildinnerHTMLinsertBefore 等方法,將資源的相對路徑替換為絕對路徑。

以下是一個例子,假設我們有一個子應用,它使用 jQuery 動態插入了一張圖片:

const render = $ => {
  $('#app-container').html('<p>Hello, render with jQuery</p><img src="./img/my-image.png">');
  return Promise.resolve();
};

我們可以在主應用中劫持 jQuery 的 html 方法,將圖片的相對路徑替換為絕對路徑:

beforeMount: app => {
   if(app.name === 'my-app'){
       // jQuery 的 html 方法是一個複雜的函數,這裏為了簡化,我們只處理 img 標籤
       $.prototype.html = function(value){
          const str = value.replace('<img src="./img/my-image.png">', '<img src="http://localhost:8080/img/my-image.png">')
          this[0].innerHTML = str;
       }
   }
}

在這個例子中,我們劫持了 jQuery 的 html 方法,將圖片的相對路徑 ./img/my-image.png 替換為了絕對路徑 http://localhost:8080/img/my-image.png。這樣,無論子應用在哪裏運行,圖片都可以正確地加載。

在使用 qiankun 時,你如何處理老項目的資源加載問題?你能給出一些具體的解決方案嗎?

在使用 qiankun 時,處理老項目的資源加載問題可以有多種方案,具體的選擇取決於項目的具體情況。以下是一些可能的解決方案:
  1. 使用 qiankungetTemplate 函數重寫靜態資源路徑:對於 HTML 中已有的 img/audio/video 等標籤,qiankun 支持重寫 getTemplate 函數,可以將入口文件 index.html 中的靜態資源路徑替換掉。例如:
start({
  getTemplate(tpl,...rest) {
    // 為了直接看到效果,所以寫死了,實際中需要用正則匹配
    return tpl.replace('<img src="./img/my-image.png">', '<img src="http://localhost:8080/img/my-image.png">');
  }
});
  1. 劫持標籤插入函數:對於動態插入的 img/audio/video 等標籤,我們可以劫持 appendChildinnerHTMLinsertBefore 等事件,將資源的相對路徑替換成絕對路徑。例如,我們可以劫持 jQuery 的 html 方法,將圖片的相對路徑替換為絕對路徑:
beforeMount: app => {
   if(app.name === 'my-app'){
       $.prototype.html = function(value){
          const str = value.replace('<img src="./img/my-image.png">', '<img src="http://localhost:8080/img/my-image.png">')
          this[0].innerHTML = str;
       }
   }
}
  1. 給老項目加上 webpack 打包:這個方案的可行性不高,都是陳年老項目了,沒必要這樣折騰。
  2. 使用 iframe 嵌入老項目:雖然 qiankun 支持 jQuery 老項目,但是似乎對多頁應用沒有很好的解決辦法。每個頁面都去修改,成本很大也很麻煩,但是使用 iframe 嵌入這些老項目就比較方便。

你能解釋一下 qiankunstart 函數的作用和參數嗎?如果只有一個子項目,你會如何啓用預加載?

qiankunstart 函數是用來啓動微前端應用的。在註冊完所有的子應用之後,我們需要調用 start 函數來啓動微前端應用。

start 函數接收一個可選的配置對象作為參數,這個對象可以包含以下屬性:

  • prefetch:預加載模式,可選值有 truefalse'all''popstate'。默認值為 true,即在主應用 start 之後即刻開始預加載所有子應用的靜態資源。如果設置為 'all',則主應用 start 之後會預加載所有子應用靜態資源,無論子應用是否激活。如果設置為 'popstate',則只有在路由切換的時候才會去預加載對應子應用的靜態資源。
  • sandbox:沙箱模式,可選值有 truefalse{ strictStyleIsolation: true }。默認值為 true,即為每個子應用創建一個新的沙箱環境。如果設置為 false,則子應用運行在當前環境下,沒有任何的隔離。如果設置為 { strictStyleIsolation: true },則會啓用嚴格的樣式隔離模式,即子應用的樣式會被完全隔離,不會影響到其他子應用和主應用。
  • singular:是否為單例模式,可選值有 truefalse。默認值為 true,即一次只能有一個子應用處於激活狀態。如果設置為 false,則可以同時激活多個子應用。
  • fetch:自定義的 fetch 方法,用於加載子應用的靜態資源。

如果只有一個子項目,要想啓用預加載,可以這樣使用 start 函數:

start({ prefetch: 'all' });

這樣,主應用 start 之後會預加載子應用的所有靜態資源,無論子應用是否激活。

在使用 qiankun 時,你如何處理 js 沙箱不能解決的 js 污染問題?

qiankunjs 沙箱機制主要是通過代理 window 對象來實現的,它可以有效地隔離子應用的全局變量,防止子應用之間的全局變量污染。然而,這種機制並不能解決所有的 js 污染問題。例如,如果我們使用 onclickaddEventListener<body> 添加了一個點擊事件,js 沙箱並不能消除它的影響。

對於這種情況,我們需要依賴於良好的代碼規範和開發者的自覺。在開發子應用時,我們需要避免直接操作全局對象,如 windowdocument。如果必須要操作,我們應該在子應用卸載時,清理掉這些全局事件和全局變量,以防止對其他子應用或主應用造成影響。

例如,如果我們在子應用中添加了一個全局的點擊事件,我們可以在子應用的 unmount 生命週期函數中移除這個事件:

export async function mount(props) {
  // 添加全局點擊事件
  window.addEventListener('click', handleClick);
}

export async function unmount() {
  // 移除全局點擊事件
  window.removeEventListener('click', handleClick);
}

function handleClick() {
  // 處理點擊事件
}

這樣,當子應用卸載時,全局的點擊事件也會被移除,不會影響到其他的子應用。

你能解釋一下 qiankun 如何實現 keep-alive 的需求嗎?

qiankun 中,實現 keep-alive 的需求有一定的挑戰性。這是因為 qiankun 的設計理念是在子應用卸載時,將環境還原到子應用加載前的狀態,以防止子應用對全局環境造成污染。這種設計理念與 keep-alive 的需求是相悖的,因為 keep-alive 需要保留子應用的狀態,而不是在子應用卸載時將其狀態清除。

然而,我們可以通過一些技巧來實現 keep-alive 的效果。一種可能的方法是在子應用的生命週期函數中保存和恢復子應用的狀態。例如,我們可以在子應用的 unmount 函數中保存子應用的狀態,然後在 mount 函數中恢復這個狀態:

// 偽代碼
let savedState;

export async function mount(props) {
  // 恢復子應用的狀態
  if (savedState) {
    restoreState(savedState);
  }
}

export async function unmount() {
  // 保存子應用的狀態
  savedState = saveState();
}

function saveState() {
  // 保存子應用的狀態
  // 這個函數的實現取決於你的應用
}

function restoreState(state) {
  // 恢復子應用的狀態
  // 這個函數的實現取決於你的應用
}

這種方法的缺點是需要手動保存和恢復子應用的狀態,這可能會增加開發的複雜性。此外,這種方法也不能保留子應用的 DOM 狀態,只能保留 JavaScript 的狀態。

還有一種就是手動*loadMicroApp*+display:none,直接隱藏Dom

另一種可能的方法是使用 single-spaParcel 功能。Parcelsingle-spa 的一個功能,它允許你在一個應用中掛載另一個應用,並且可以控制這個應用的生命週期。通過 Parcel,我們可以將子應用掛載到一個隱藏的 DOM 元素上,從而實現 keep-alive 的效果。然而,這種方法需要對 qiankun 的源碼進行修改,因為 qiankun 目前並不支持 Parcel

你能解釋一下 qiankuniframe 在微前端實現方式上的區別和優劣嗎?在什麼情況下,你會選擇使用 iframe 而不是 qiankun

qiankuniframe 都是微前端的實現方式,但它們在實現原理和使用場景上有一些區別。

qiankun 是基於 single-spa 的微前端解決方案,它通過 JavaScript 的 import 功能動態加載子應用,然後在主應用的 DOM 中掛載子應用的 DOM。qiankun 提供了一種 JavaScript 沙箱機制,可以隔離子應用的全局變量,防止子應用之間的全局變量污染。此外,qiankun 還提供了一種樣式隔離機制,可以防止子應用的 CSS 影響其他應用。這些特性使得 qiankun 在處理複雜的微前端場景時具有很高的靈活性。

iframe 是一種較為傳統的前端技術,它可以在一個獨立的窗口中加載一個 HTML 頁面。iframe 本身就是一種天然的沙箱,它可以完全隔離子應用的 JavaScript 和 CSS,防止子應用之間的相互影響。然而,iframe 的這種隔離性也是它的缺點,因為它使得主應用和子應用之間的通信變得困難。此外,iframe 還有一些其他的問題,比如性能問題、SEO 問題等。

在選擇 qiankuniframe 時,需要根據具體的使用場景來決定。如果你的子應用是基於現代前端框架(如 React、Vue、Angular 等)開發的單頁應用,那麼 qiankun 可能是一個更好的選擇,因為它可以提供更好的用户體驗和更高的開發效率。如果你的子應用是基於 jQuery 或者其他傳統技術開發的多頁應用,或者你需要在子應用中加載一些第三方的頁面,那麼 iframe 可能是一個更好的選擇,因為它可以提供更強的隔離性。

在使用 qiankun 時,你如何處理多個子項目的調試問題?

在使用qiankun處理多個子項目的調試問題時,通常的方式是將每個子項目作為一個獨立的應用進行開發和調試。每個子項目都可以在本地啓動,並通過修改主應用的配置,讓主應用去加載本地正在運行的子應用,這樣就可以對子應用進行調試了。這種方式的好處是,子應用與主應用解耦,可以獨立進行開發和調試,不會相互影響。

對於如何同時啓動多個子應用,你可以使用npm-run-all這個工具。npm-run-all是一個CLI工具,可以並行或者串行執行多個npm腳本。這個工具對於同時啓動多個子應用非常有用。使用方式如下:

  1. 首先,你需要在你的項目中安裝npm-run-all,可以通過下面的命令進行安裝:
npm install --save-dev npm-run-all
  1. 然後,在你的package.json文件中定義你需要並行運行的腳本。比如,你有兩個子應用,分別為app1app2,你可以定義如下的腳本:
"scripts": {
    "start:app1": "npm start --prefix ./app1",
    "start:app2": "npm start --prefix ./app2",
    "start:all": "npm-run-all start:app1 start:app2"
}

在這個例子中,start:app1start:app2腳本分別用於啓動app1app2應用,start:all腳本則用於同時啓動這兩個應用。

  1. 最後,通過執行npm run start:all命令,就可以同時啓動app1app2這兩個應用了。

npm-run-all不僅可以並行運行多個腳本,還可以串行運行多個腳本。在某些情況下,你可能需要按照一定的順序啓動你的應用,這時你可以使用npm-run-all-s選項來串行執行腳本,例如:npm-run-all -s script1 script2,這將會先執行script1,然後再執行script2

qiankun是如何實現CSS隔離的,該方案有什麼缺點,還有其它方案麼

qiankun主要通過使用Shadow DOM來實現CSS隔離。

  1. Shadow DOMShadow DOM是一種瀏覽器內置的Web標準技術,它可以創建一個封閉的DOM結構,這個DOM結構對外部是隔離的,包括其CSS樣式。qiankun在掛載子應用時,會將子應用的HTML元素掛載到Shadow DOM上,從而實現CSS的隔離。
// qiankun使用Shadow DOM掛載子應用
const container = document.getElementById('container');
const shadowRoot = container.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<div id="subapp-container"></div>';

對於qiankun的隔離方案,一個潛在的缺點是它需要瀏覽器支持Shadow DOM,這在一些舊的瀏覽器或者不兼容Shadow DOM的瀏覽器中可能會出現問題。

另一種可能的方案是使用CSS模塊(CSS Modules)。CSS模塊是一種將CSS類名局部化的方式,可以避免全局樣式衝突。在使用CSS模塊時,每個模塊的類名都會被轉換成一個唯一的名字,從而實現樣式的隔離。

例如,假設你有一個名為Button的CSS模塊:

/* Button.module.css */
.button {
    background-color: blue;
}

在你的JavaScript文件中,你可以這樣引入並使用這個模塊:

import styles from './Button.module.css';

function Button() {
    return <button className={styles.button}>Click me</button>;
}

在這個例子中,button類名會被轉換成一個唯一的名字,如Button_button__xxx,這樣就可以避免全局樣式衝突了。

3.BEM命名規範隔離

qiankun中如何實現父子項目間的通信?如果讓你實現一套通信機制,你該如何實現?

  • Actions 通信:qiankun 官方提供的通信方式,適合業務劃分清晰,較簡單的微前端應用。這種通信方式主要通過 setGlobalState 設置 globalState,並通過 onGlobalStateChangeoffGlobalStateChange 來註冊和取消 觀察者 函數,從而實現通信。
  • 自己實現一套通信機制(可以思考一下如何追蹤State狀態,類似Redux模式)
  1. 全局變量:在全局(window)對象上定義共享的屬性或方法。這種方式簡單明瞭,但有可能導致全局污染,需要注意變量命名以避免衝突。
  2. 自定義事件:使用原生的 CustomEvent 或類似的第三方庫來派發和監聽自定義事件。這種方式避免了全局污染,更加符合模塊化的原則,但可能需要更復雜的事件管理。
  • 2.1. 定義一個全局的通信對象,例如 window.globalEvent,這個對象提供兩個方法,emit 和 on。
  • 2.2. emit 方法用於派發事件,接收事件名稱和可選的事件數據作為參數。

    - 2.3. **on 方法**用於監聽事件,接收事件名稱和回調函數作為參數。當相應的事件被派發時,回調函數將被執行。
    
window.globalEvent = {
  events: {},
  emit(event, data) {
    if (!this.events[event]) {
      return;
    }
    this.events[event].forEach(callback => callback(data));
  },
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  },
};

1.在主項目中使用qiankun註冊子項目時,如何解決子項目路由的hash與history模式之爭?

如果主項目使用 history 模式,並且子項目可以使用 historyhash 模式,這是 qiankun 推薦的一種形式。在這種情況下,子項目可以選擇適合自己的路由模式,而且對於已有的子項目不需要做太多修改。但是子項目之間的跳轉需要通過父項目的 router 對象或原生的 history 對象進行。

2. 如果主項目和所有子項目都採用 hash 模式,可以有兩種做法:

  • 使用 path 來區分子項目:這種方式不需要對子項目進行修改,但所有項目之間的跳轉需要藉助原生的 history 對象。
  • 使用 hash 來區分子項目:這種方式可以通過自定義 activeRule 來實現,但需要對子項目進行一定的修改,將子項目的路由加上前綴。這樣的話,項目之間的跳轉可以直接使用各自的 router 對象或 <router-link>

3. 如果主項目採用 hash 模式,而子項目中有些採用 history 模式,這種情況下,子項目間的跳轉只能藉助原生的 history 對象,而不使用子項目自己的 router 對象。對於子項目,可以選擇使用 pathhash 來區分不同的子項目。

在qiankun中,如果實現組件在不同項目間的共享,有哪些解決方案?

在項目間共享組件時,可以考慮以下幾種方式:
  1. 父子項目間的組件共享:主項目加載時,將組件掛載到全局對象(如window)上,在子項目中直接註冊使用該組件。
  2. 子項目間的組件共享(弱依賴):通過主項目提供的全局變量,子項目掛載到全局對象上。子項目中的共享組件可以使用異步組件來實現,在加載組件前先檢查全局對象中是否存在,存在則複用,否則加載組件。
  3. 子項目間的組件共享(強依賴):在主項目中通過loadMicroApp手動加載提供組件的子項目,確保先加載該子項目。在加載時,將組件掛載到全局對象上,並將loadMicroApp函數傳遞給子項目。子項目在需要使用共享組件的地方,手動加載提供組件的子項目,等待加載完成後即可獲取組件。

需要注意的是,在使用異步組件或手動加載子項目時,可能會遇到樣式加載的問題,可以嘗試解決該問題。另外,如果共享的組件依賴全局插件(如storei18n),需要進行特殊處理以確保插件的正確初始化。

在qiankun中,應用之間如何複用依賴,除了npm包方案外?

  1. 在使用webpack構建的子項目中,要實現複用公共依賴,需要配置webpackexternals,將公共依賴指定為外部依賴,不打包進子項目的代碼中。
  2. 子項目之間的依賴複用可以通過保證依賴的URL一致來實現。如果多個子項目都使用同一份CDN文件,加載時會先從緩存讀取,避免重複加載。
  3. 子項目複用主項目的依賴可以通過給子項目的index.html中的公共依賴的scriptlink標籤添加自定義屬性ignore來實現。在qiankun運行子項目時,qiankun會忽略這些帶有ignore屬性的依賴,子項目獨立運行時仍然可以加載這些依賴。
  4. 在使用qiankun微前端框架時,可能會出現子項目之間和主項目之間的全局變量衝突的問題。這是因為子項目不配置externals時,子項目的全局Vue變量不屬於window對象,而qiankun在運行子項目時會先找子項目的window,再找父項目的window,導致全局變量衝突。
  5. 解決全局變量衝突的方案有三種:

    • 方案一是在註冊子項目時,在beforeLoad鈎子函數中處理全局變量,將子項目的全局Vue變量進行替換,以解決子項目獨立運行時的全局變量衝突問題。
    • 方案二是通過主項目將依賴通過props傳遞給子項目,子項目在獨立運行時使用傳遞過來的依賴,避免與主項目的全局變量衝突。
    • 方案三是修改主項目和子項目的依賴名稱,使它們不會相互衝突,從而避免全局變量衝突的問題。

説説webpack5聯邦模塊在微前端的應用

Webpack 5 的聯邦模塊(Federation Module)是一個功能強大的特性,可以在微前端應用中實現模塊共享和動態加載,從而提供更好的代碼複用和可擴展性

1. 模塊共享

Webpack 5 的聯邦模塊允許不同的微前端應用之間共享模塊,避免重複加載和代碼冗餘。通過聯邦模塊,我們可以將一些公共的模塊抽離成一個獨立的模塊,並在各個微前端應用中進行引用。這樣可以節省資源,並提高應用的加載速度。

// main-app webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...其他配置

  plugins: [
    new HtmlWebpackPlugin(),
    new ModuleFederationPlugin({
      name: 'main_app',
      remotes: {
        shared_module: 'shared_module@http://localhost:8081/remoteEntry.js',
      },
    }),
  ],
};

// shared-module webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...其他配置

  plugins: [
    new ModuleFederationPlugin({
      name: 'shared_module',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button',
      },
    }),
  ],
};

在上述示例中,main-appshared-module 分別是兩個微前端應用的 webpack 配置文件。通過 ModuleFederationPlugin 插件,shared-moduleButton 組件暴露給其他應用使用,而 main-app 則通過 remotes 配置引入了 shared-module

2. 動態加載

Webpack 5 聯邦模塊還支持動態加載模塊,這對於微前端應用的按需加載和性能優化非常有用。通過動態加載,可以在需要時動態地加載遠程模塊,而不是在應用初始化時一次性加載所有模塊。

// main-app
const remoteModule = () => import('shared_module/Button');

// ...其他代碼

// 在需要的時候動態加載模塊
remoteModule().then((module) => {
  // 使用加載的模塊
  const Button = module.default;
  // ...
});

在上述示例中,main-app 使用 import() 函數動態加載 shared_module 中的 Button 組件。通過動態加載,可以在需要時異步地加載遠程模塊,並在加載完成後使用模塊。

在微前端應用中可以實現模塊共享和動態加載,提供了更好的代碼複用和可擴展性。通過模塊共享,可以避免重複加載和代碼冗餘,而動態加載則可以按需加載模塊,提高應用的性能和用户體驗。

説説qiankun的資源加載機制(import-html-entry)

qiankun import-html-entry 是qiankun 框架中用於加載子應用的 HTML 入口文件的工具函數。它提供了一種方便的方式來動態加載和解析子應用的 HTML 入口文件,並返回一個可以加載子應用的 JavaScript 模塊。

具體而言,import-html-entry 實現了以下功能:

    1. 加載 HTML 入口文件:import-html-entry 會通過創建一個 <link> 標籤來加載子應用的 HTML 入口文件。這樣可以確保子應用的資源得到正確加載,並在加載完成後進行處理。
    1. 解析 HTML 入口文件:一旦 HTML 入口文件加載完成,import-html-entry 將解析該文件的內容,提取出子應用的 JavaScript 和 CSS 資源的 URL。
    1. 動態加載 JavaScript 和 CSS 資源:import-html-entry 使用動態創建 <script><link> 標籤的方式,按照正確的順序加載子應用的 JavaScript 和 CSS 資源。
    1. 創建沙箱環境:在加載子應用的 JavaScript 資源時,import-html-entry 會創建一個沙箱環境(sandbox),用於隔離子應用的全局變量和運行環境,防止子應用之間的衝突和污染。
    1. 返回子應用的入口模塊:最後,import-html-entry 返回一個可以加載子應用的 JavaScript 模塊。這個模塊通常是一個包含子應用初始化代碼的函數,可以在主應用中調用以加載和啓動子應用。

通過使用 qiankun import-html-entry,開發者可以方便地將子應用的 HTML 入口文件作為模塊加載,並獲得一個可以加載和啓動子應用的函數,簡化了子應用的加載和集成過程。

説説現有的幾種微前端框架,它們的優缺點?

以下是對各個微前端框架優缺點的總結:

  1. qiankun 方案

    優點

    • 降低了應用改造的成本,通過html entry的方式引入子應用;
    • 提供了完備的沙箱方案,包括js沙箱和css沙箱;
    • 支持靜態資源預加載能力。

    缺點

    • 適配成本較高,包括工程化、生命週期、靜態資源路徑、路由等方面的適配;
    • css沙箱的嚴格隔離可能引發問題,js沙箱在某些場景下執行性能下降;
    • 無法同時激活多個子應用,不支持子應用保活;
    • 不支持vite等esmodule腳本運行。
  2. micro-app 方案

    優點

    • 使用 webcomponent 加載子應用,更優雅;
    • 複用經過大量項目驗證過的 qiankun 沙箱機制,提高了框架的可靠性;
    • 支持子應用保活;
    • 降低了子應用改造的成本,提供了靜態資源預加載能力。

    缺點

    • 接入成本雖然降低,但路由依然存在依賴;
    • 多應用激活後無法保持各子應用的路由狀態,刷新後全部丟失;
    • css 沙箱無法完全隔離,js 沙箱做全局變量查找緩存,性能有所優化;
    • 支持 vite 運行,但必須使用 plugin 改造子應用,且 js 代碼沒辦法做沙箱隔離;
    • 對於不支持 webcomponent 的瀏覽器沒有做降級處理。
  3. EMP 方案

    優點

    • webpack 聯邦編譯可以保證所有子應用依賴解耦;
    • 支持應用間去中心化的調用、共享模塊;
    • 支持模塊遠程 ts 支持。

    缺點

    • 對 webpack 強依賴,對於老舊項目不友好;
    • 沒有有效的 css 沙箱和 js 沙箱,需要靠用户自覺;
    • 子應用保活、多應用激活無法實現;
    • 主、子應用的路由可能發生衝突。
  4. 無界方案

    優點

    • 基於 webcomponent 容器和 iframe 沙箱,充分解決了適配成本、樣式隔離、運行性能、頁面白屏、子應用通信、子應用保活、多應用激活、vite框架支持、應用共享等問題。

    缺點

    • 在繼承了iframe優點的同時,缺點依舊還是存在
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.