前端-微前端個(gè)人筆記

微前端的幾個(gè)常用框架:single-spa , 基于single-spa的qiankun。
微前端實(shí)現(xiàn)方式:

1.服務(wù)端集成

通過nginx配置反向代理來實(shí)現(xiàn)不同路徑映射到不同應(yīng)用,

1.png

-部分核心代碼

server {
    listen 80;
    server_name example.com;
 
    location /app1/ {
        proxy_pass http://app1-server;
    }
 
    location /app2/ {
        proxy_pass http://app2-server;
    }
}
const express = require('express');
const app = express();
const httpProxy = require('http-proxy');
const apiProxy = httpProxy.createProxyServer({});
 
app.use('/app1', (req, res) => {
    apiProxy.web(req, res, { target: 'http://app1-server' });
});
 
app.use('/app2', (req, res) => {
    apiProxy.web(req, res, { target: 'http://app2-server' });
});
 
app.listen(3000, () => {
    console.log('Server running on port 3000');
});

個(gè)人理解:其實(shí)就是把兩個(gè)應(yīng)用分別映射到兩個(gè)url路徑下,用戶輸入/app1時(shí),就會(huì)請(qǐng)求app1的前端,輸入/app2時(shí),就會(huì)請(qǐng)求到app2的前端。
弊端:丟失spa的體驗(yàn),每次命中路由都會(huì)重新請(qǐng)求資源,不能局部更新當(dāng)前頁面。

2.運(yùn)行時(shí)集成

使用iframe,配置不同的src加載不同的子應(yīng)用
-部分核心代碼
主應(yīng)用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>主應(yīng)用</title>
</head>

<body>
    <h1>主應(yīng)用頁面</h1>
    <!-- 嵌入子應(yīng)用 -->
    <iframe id="subApp" src="sub-app.html" width="100%" height="500px"></iframe>
    <script>
        // 示例:主應(yīng)用向子應(yīng)用發(fā)送消息
        const iframe = document.getElementById('subApp');
        iframe.contentWindow.postMessage('來自主應(yīng)用的消息', '*');

        // 監(jiān)聽子應(yīng)用發(fā)送的消息
        window.addEventListener('message', (event) => {
            console.log('收到子應(yīng)用的消息:', event.data);
        });
    </script>
</body>

</html>

子應(yīng)用

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>子應(yīng)用</title>
</head>

<body>
    <h1>子應(yīng)用頁面</h1>
    <script>
        // 監(jiān)聽主應(yīng)用發(fā)送的消息
        window.addEventListener('message', (event) => {
            console.log('收到主應(yīng)用的消息:', event.data);
            // 子應(yīng)用向主應(yīng)用發(fā)送消息
            window.parent.postMessage('來自子應(yīng)用的消息', '*');
        });
    </script>
</body>

</html>

核心在于傳遞信息
(1).主應(yīng)用傳遞信息
iframe.contentWindow.postMessage
(2).子應(yīng)用接受信息
window.addEventListener('message', (event) => {
});
(3).子應(yīng)用傳遞信息給主應(yīng)用
window.parent.postMessage('來自子應(yīng)用的消息', '*');
(4).兩個(gè)子應(yīng)用之間傳遞
兩個(gè)子應(yīng)用之間通過主應(yīng)用進(jìn)行傳遞
用localstorage 或者sessionstorage

缺點(diǎn)
(1).iframe功能之間的跳轉(zhuǎn)是無效的,刷新頁面無法保存狀態(tài)。
(2).url記錄完全無效。
(3).主應(yīng)用劫持快捷鍵操作,事件冒泡不穿透到主文檔樹上。
(4).模態(tài)彈窗的背景是無法覆蓋到整個(gè)應(yīng)用。
(5).iframe應(yīng)用加載失敗,內(nèi)容發(fā)生錯(cuò)誤主應(yīng)用無法感知,通信麻煩。

3.現(xiàn)有比較流行的解決方案

1.single-spa *

single-spa定義了一套協(xié)議,協(xié)議包含主應(yīng)用的配置信息和子應(yīng)用的生命周期,通過這套協(xié)議,主應(yīng)用可以方便的知道在什么情況下激活哪個(gè)子應(yīng)用。

 singleSpa.registerApplication(
{ 
name: 'appName', //子應(yīng)用的名稱
app: () =>System.import('appName'), //告訴主應(yīng)用如何加載子應(yīng)用的代碼
 activeWhen: '/appName', //告訴主應(yīng)用何時(shí)激活子應(yīng)用
})

(1).system.import
主應(yīng)用要想加載子應(yīng)用,首先要在子應(yīng)用中實(shí)現(xiàn)生命周期函數(shù),然后導(dǎo)出給主應(yīng)用,變成一個(gè)帶有生命周期的模塊,涉及到j(luò)s的模塊化。以下是js模塊化的一些知識(shí)點(diǎn)。
commonjs規(guī)范
同步加載,
AMD
異步加載,加載完就執(zhí)行
CMD
異步加載,使用的時(shí)候才會(huì)執(zhí)行

(2).single-spa加載流程

1.在主應(yīng)用中配置信息,主應(yīng)用會(huì)根據(jù)配置信息去請(qǐng)求子應(yīng)用的manifest.json配置文件,這個(gè)文件是子應(yīng)用打包的入口js和文件的依賴關(guān)系,主應(yīng)用通過動(dòng)態(tài)狗仔scrip標(biāo)簽去加載這些js文件,完成注冊(cè)過程。
2.檢測(cè)路由命中應(yīng)用的規(guī)則之后,就會(huì)觸發(fā)其下渲染函數(shù),掛載到相應(yīng)的dom下。

2.png

(3).single-spa生命周期
single-spa關(guān)鍵點(diǎn)生命周期,子應(yīng)用包含bootstrap,mount,unmount三個(gè)生命周期,通過子應(yīng)用暴露的生命周期函數(shù)來實(shí)現(xiàn)子應(yīng)用的啟動(dòng)和卸載。

image.png

(4).single-spa缺陷
1.通過js文件加載子應(yīng)用,文件名是亂碼時(shí),子應(yīng)用更新后,父應(yīng)用需要重新引入配置,比較麻煩
2.缺少js隔離和css隔離。

2.qiankun **

qiankun基于single-spa,但又有些許不同,single-spa注冊(cè)子應(yīng)用本質(zhì)是js enrty,通過某一地址引入js文件夾來加載整個(gè)子應(yīng)用,但是qiankun注冊(cè)子應(yīng)用的方式是通過一個(gè)url,即使用html enrty 來引入子應(yīng)用

registerMicroApps([{ name: 'react app', entry: '//localhost:7100', container: '#yourContainer', activeRule: '/yourActiveRule' },]);
start();

url一般是固定的,所以不用頻繁更新主應(yīng)用中的注冊(cè)信息,便于整合和開發(fā)。

(1).樣式隔離
shadowdom
(2).js隔離
快照沙箱
legacy沙箱(es6-proxy)
(3).生命周期

3.Module Federation以及EMP **

Module Federation是webpack5中的新特性,主要是用來解決多個(gè)應(yīng)用之間代碼共享的問題,可以更加優(yōu)雅的實(shí)現(xiàn)跨應(yīng)用的代碼共享,使用這個(gè)方法也可以實(shí)現(xiàn)微前端。

這個(gè)方案中有兩個(gè)主體:Remote和Host,可以把Remote理解為想要引入的子應(yīng)用,把Host理解為主應(yīng)用(但是一個(gè)應(yīng)用既可以是Remote也可以是Host,并不矛盾)。

Module Federation的核心在于ModuleFederationPlugin這個(gè)插件:

new ModuleFederationPlugin({ 
    name: "App1",  //必須,唯一ID,作為輸出的模塊名,使用的時(shí)通過name/{expose}的方式使用;
    library: { type: "var", name: "App1" }, //必須,其中這里的name為作為umd的name。
    filename: "remoteEntry.js",
    remotes: { app_02: 'App2', app_03: 'App3', },  //可選,表示作為Host時(shí),去消費(fèi)哪些Remote。
    exposes: { antd: './src/antd', button:'./src/button', }, 可選,表示作為Remote時(shí),export哪些屬性被消費(fèi)。
    shared: ['react', 'react-dom'], })//可選,優(yōu)先用Host的依賴,如果Host沒有,再用自己的。

qiankun學(xué)習(xí)與實(shí)戰(zhàn)

以下詳細(xì)對(duì)qiankun進(jìn)行一個(gè)學(xué)習(xí)歸納,基于文檔,學(xué)習(xí)視頻和自身理解。
qiankun學(xué)習(xí)文檔

### 詳細(xì)描述一下 qiankun 微前端框架的工作原理

qiankuan是基于single-spa的微前端實(shí)現(xiàn)框架,工作原理主要涉及:

1.應(yīng)用加載
通過動(dòng)態(tài)創(chuàng)建script標(biāo)簽的方式加載子應(yīng)用的入口文件,加載完成后,會(huì)執(zhí)行子應(yīng)用暴露出來的生命周期函數(shù)。
2.生命周期管理
子應(yīng)用需要暴露出bootstrap,mount和unmount三個(gè)生命周期函數(shù),bootstrap在子應(yīng)用加載時(shí)被調(diào)用,mount函數(shù)在子應(yīng)用啟動(dòng)時(shí)被調(diào)用,unmount函數(shù)在子應(yīng)用卸載時(shí)被調(diào)用。
3.沙箱隔離
qiankun通過Proxy對(duì)象創(chuàng)建了一個(gè)js沙箱,用戶隔離子應(yīng)用的全局變量,防止子應(yīng)用之間的全局變量污染。
4.樣式隔離
qiankun通過動(dòng)態(tài)添加和移除樣式標(biāo)簽的方式實(shí)現(xiàn)了樣式隔離,當(dāng)子應(yīng)用啟動(dòng)時(shí),會(huì)動(dòng)態(tài)添加子應(yīng)用的標(biāo)簽樣式,卸載時(shí),會(huì)移除子應(yīng)用的標(biāo)簽樣式。
qiankun 提供了快照沙箱代理沙箱兩種模式。代理沙箱基于 ES6 的 Proxy 對(duì)象實(shí)現(xiàn),部分舊版本瀏覽器(如 IE 系列)不支持 Proxy,只能使用快照沙箱。而快照沙箱在處理復(fù)雜場(chǎng)景時(shí)可能存在局限性,并且在某些情況下恢復(fù)全局狀態(tài)時(shí)可能會(huì)出現(xiàn)不一致的問題。
5.通信機(jī)制
qiankun提供了一個(gè)全局的通信機(jī)制,允許應(yīng)用之間進(jìn)行通信。

qiankun資源加載機(jī)制

import-html-entry是qiankun中用于加載子應(yīng)用的html入口文件的工具函數(shù),提供了一種方便的方式來動(dòng)態(tài)加載和解析子應(yīng)用的html入口文件,并返回一個(gè)可以加載子應(yīng)用的js模塊。

import-html-entry實(shí)現(xiàn)了以下功能:
1.加載html入口文件
import-html-entry會(huì)創(chuàng)建一個(gè)<link>標(biāo)簽來加載子應(yīng)用的html入口文件,這樣可以確保子應(yīng)用的資源得到正確加載,并在加載后處理。
2.解析html入口文件
加載完成后,import-html-entry會(huì)解析該文件的內(nèi)容,提取出子應(yīng)用的js和cs資源的url。
3.動(dòng)態(tài)加載js和css資源
import-html-entry使用動(dòng)態(tài)創(chuàng)建<script>和<link>標(biāo)簽的方式,按照正確的順序加載子應(yīng)用的js和css資源
4.創(chuàng)建沙箱環(huán)境
在加載子應(yīng)用的js資源時(shí),impor-html-entry會(huì)創(chuàng)建一個(gè)沙箱環(huán)境(sandbox),用于隔離子應(yīng)用的全局變量和運(yùn)行環(huán)境,防止子應(yīng)用之間的沖突和污染。
5.返回子應(yīng)用的入口模塊
最后,import-html-entry 返回一個(gè)加載子應(yīng)用的js模塊,這個(gè)模塊通常是一個(gè)包含子應(yīng)用初始化代碼的函數(shù),可以在主營(yíng)用中調(diào)用以加載和啟動(dòng)子應(yīng)用。

項(xiàng)目目錄:
3.png
  • micro-base 基座(主應(yīng)用):主要負(fù)責(zé)集成所有的子應(yīng)用

  • app-react,vue-react (子應(yīng)用):根據(jù)不同業(yè)務(wù)劃分的模塊,每個(gè)子應(yīng)用都打包成umd模塊的形式供基座來加載

  • 互相通信

1.localstorage sessionstorage 通信 (在app-react,micro-base中通信)

localStorage.setItem("storage-token", 'my-storage-token')

在子應(yīng)用中通過

localStorage.getItem('storage-token)

2.通過路由參數(shù)共享 (在app-vue,micro-app中通信)
3、官方提供的 props
a.父?jìng)髯?/p>

  {
    name: 'reactApp',
    entry: '//localhost:3001',
    container: '#container',
    activeRule: '/app-react',
    props: {
      qiankunProp: 'qiankun-prop-token',
      changeQiankunProp: changeQiankunProp //子傳父所需要的事件
    }  
 },

子在子應(yīng)用生命周期里通過props獲取
b.子傳父
props中的事件,與react通信類似
4.官方提供的 actions

// 初始化 state
const state = {
  actionsToken: 'my-actions-token'
}

const actions = initGlobalState(state);

actions.onGlobalStateChange((state, prev) => {
  // state: 變更后的狀態(tài); prev 變更前的狀態(tài)
  console.log('actions改變狀態(tài)', state, prev)
});
actions.setGlobalState(state);
actions.offGlobalStateChange()

5、使用vuex或redux管理狀態(tài),通過shared分享
子應(yīng)用通過狀態(tài)管理工具進(jìn)行管理,然后通過class shared暴露給父應(yīng)用?

  • 樣式隔離
    1.starts中利用qiankun的自帶參數(shù)
start({
  sandbox: {
    // 開啟嚴(yán)格的樣式隔離模式。這種模式下 qiankun 會(huì)為每個(gè)微應(yīng)用的容器包裹上一個(gè) [shadow dom]節(jié)點(diǎn),從而確保微應(yīng)用的樣式不會(huì)對(duì)全局造成影響。
    strictStyleIsolation: false,
    // 設(shè)置實(shí)驗(yàn)性的樣式隔離特性,即在子應(yīng)用下面的樣式都會(huì)包一個(gè)特殊的選擇器規(guī)則來限定其影響范圍
    experimentalStyleIsolation: true
  }
})
  • 公共包重復(fù)加載
    TODO

未完待續(xù)??!
代碼后續(xù)會(huì)上傳,并給出鏈接~~

參考資料:
微前端究竟是什么?微前端核心技術(shù)揭秘!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容