qiankun學習記錄

什么是微前端

微前端是指存在于瀏覽器中的微服務,其借鑒了微服務的架構理念,將微服務的概念擴展到了前端。

如果對微服務的概念比較陌生的話,可以簡單的理解為微前端就是將一個大型的前端應用拆分成多個模塊,每個微前端模塊可以由不同的團隊進行管理,并可以自主選擇框架,并且有自己的倉庫,可以獨立部署上線。

微前端的好處

1.團隊自治
2.兼容老項目
3.跨技術棧

現有的微前端方案

1.iframe
通過iframe標簽來嵌入到父應用中,iframe具有天然的隔離屬性,各個子應用之間以及子應用和父應用之間都可以做到互不影響。

iframe的缺點:

  1. url不同步,如果刷新頁面,iframe中的頁面的路由會丟失。
  2. 全局上下文完全隔離,內存變量不共享。
  3. UI不同步,比如iframe中的頁面如果有帶遮罩層的彈窗組件,則遮罩就不能覆蓋整個瀏覽器,只能在iframe中生效。
  4. 慢。每次子應用進入都是一次瀏覽器上下文重建、資源重新加載的過程。
2.single-spa
官網:https://zh-hans.single-spa.js.org/docs/getting-started-overviewsingle-spa是最早的微前端框架,可以兼容很多技術棧。

single-spa的缺點:

  1. 沒有實現js隔離和css隔離
  2. 需要修改大量的配置,包括基座和子應用的,不能開箱即用
3.qiankun
qiankun是阿里開源的一個微前端的框架qiankun的優點:
- 基于single-spa封裝的,提供了更加開箱即用的API
- 技術棧無關,任意技術棧的應用均可使用/接入,不論是 React/Vue/Angular/JQuery 還是其他等框架。
- HTML Entry的方式接入,像使用iframe一樣簡單
- 實現了single-spa不具備的樣式隔離和js隔離
- 資源預加載,在瀏覽器空閑時間預加載未打開的微應用資源,加速微應用打開速度。
  • 基座(主應用):主要負責集成所有的子應用,提供一個入口能夠訪問你所需要的子應用的展示,盡量不寫復雜的業務邏輯
  • 子應用:根據不同業務劃分的模塊,每個子應用都打包成umd模塊的形式供基座(主應用)來加載
基座改造

基座用的是create-react-app腳手架加上antd組件庫搭建的項目,也可以選擇vue或者其他框架,一般來說,基座只提供加載子應用的容器,盡量不寫復雜的業務邏輯。

  1. 安裝qiankun
// 安裝qiankun
npm i qiankun // 或者 yarn add qiankun
  1. 修改入口文件
// 在src/index.tsx中增加如下代碼
import { start, registerMicroApps} from 'qiankun'
// 1.要加載的子應用列表
const apps = [{name: 'sub-react', // 子應用的名稱entry: '//localhost:3001', // 默認會加載這個路徑下的html,解析里面的jsactiveRule: '/sub-react',// 匹配的路由container: '#sub-app' // 子應用加載的容器},
] // 2. 注冊子應用
registerMicroApps(apps, {beforeLoad: [async app => console.log('before load', app.name)],beforeMount: [async app => console.log('before mount', app.name)],afterMount: [async app => console.log('after mount', app.name)],afterUnmount: [async app => console.log('after unmount', app.name)]
})// 3. 啟動微服務
start()
react子應用

使用create-react-app腳手架創建,webpack進行配置,為了不eject所有的webpack配置,我們選擇用react-app-rewired工具來改造webpack配置。
2. 修改入口文件

// 在src/index.tsx中增加如下代碼
import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom'
import './public-path'let root: any;
// 將render方法用函數包裹,供后續主應用與獨立運行調用
function render(props: any) {const { container } = props// 1.拿到root的divconst dom = container ? container.querySelector('#root') : document.getElementById('root')root = createRoot(dom)// 2.把app渲染到root上面// basename對應的是基座里面子應用列表的路由// 因為基座加載子應用的時候是匹配路由的root.render(<BrowserRouter basename='/sub-react'><App/></BrowserRouter>)
}// 判斷是否在qiankun環境下,非qiankun環境下獨立運行
if(!(window as any).__POWERED_BY_QIANKUN__) {render({})
}// 各個生命周期
// bootstrap 置灰在微應用初始化的時候調用一次,下次微應用重新進入時會直接調用mount鉤子,不會再重復觸發 bootstrap
export async function bootstrap() {console.log('react app bootstrap');
}// 應用每次進入都會調用mount方法,通常我們在這里觸發應用的渲染方法
export async function mount(props: any) {console.log('props==', props);render(props)
} // 應用每次 切出/卸載 會調用的方法,通常在這里我們會卸載微應用的應用實例
export async function unmount(props: any) {root.unmount()
}
  1. 新增public-path.js
if (window.__POWERED_BY_QIANKUN__) {// 動態設置 webpack publicPath,防止資源加載出錯// eslint-disable-next-line no-undef__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
  1. 修改webpack配置文件
// 在根目錄下新增config-overrides.js文件并新增如下配置
const { name } = require("./package");module.exports = {webpack: (config) => {config.output.library = `${name}-[name]`;// 把項目打包成umd模塊,方便qiankun去讀取我們暴露出來的生命周期(bootstrap、mount、unmount)config.output.libraryTarget = "umd";config.output.chunkLoadingGlobal = `webpackJsonp_${name}`;return config;}
};
vue子應用
# 創建子應用,選擇vue3+vite
npm create vite@latest
改造子應用
  1. 安裝vite-plugin-qiankun依賴包
npm i vite-plugin-qiankun # yarn add vite-plugin-qiankun
  1. 修改vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun';export default defineConfig({base: '/sub-vue', // 和基座中配置的activeRule一致server: {port: 3002, // 端口cors: true, // 允許跨域origin: 'http://localhost:3002'  // 指定允許跨域請求的來源地址},plugins: [vue(),// 加一個qiankun,寫子應用的名稱,需要開發模式的需要配置useDevModeqiankun('sub-vue', { // 配置qiankun插件// 是否運行在開發模式下useDevMode: true})]
})
  1. 修改main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'let app: any;
// 判斷是不是在qiankun環境下
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {createApp(App).use(router).mount('#app');
} else {renderWithQiankun({// 子應用掛載(來回切換的時候)mount (props){ // 掛載的時候app = createApp(App);app.use(router).mount(props.container.querySelector('#app'))},// 只有子應用第一次加載會觸發bootstrap () { // 應用剛加載的時候console.log('vue app bootstrap');},// 更新update () { // 更新console.log('vue app update');},// 卸載unmount () { // 卸載console.log('vue app unmount');app?.unmount();}})
}
umi子應用

使用umi4去創建子應用,創建好后只需要簡單的配置就可以跑起來

  1. 安裝插件
npm i @umijs/plugins
  1. 配置.umirc.ts
export default {base: '/sub-umi',npmClient: 'npm',plugins: ['@umijs/plugins/dist/qiankun'],qiankun: {slave: {},}
};
// 在入口文件導出qiankun的生命周期
export const qiankun = {async mount(props:any) {console.log(props);},async bootstrap () {console.log('umi app bootstraped');},async afterMount(props: any) {console.log('umi app afterMount', props);},
}

補充

1.樣式隔離

qiankun內部實現的是子應用之間的樣式隔離,基座和子應用之間的樣式么沒有進行隔離

應用間的樣式隔離原因:子應用之間的樣式隔離很簡單,加載子應用的樣式,卸載的時候把子應用的樣式進行卸載,加載下一個子應用的時候加載下一個子應用的樣式

 1.樣式隔離1.1 每個應用的樣式使用固定的格式1.2 通過css-module的方式給每個應用自動添加上前綴修改基座應用公共樣式的時候還是會影響子應用的樣式,這時候可以把子應用的樣式優先級提高一點 (樣式隔離)
2.子應用間的跳轉

2.1 主應用和微應用都是hash模式,主應用根據hash來判斷微應用,則不用考慮這個問題

通過location.hash直接修改hash值
http://localhost:3001/#/react-app/list
修改為
http://localhost:3001/#/vue-app/list

2.2 history模式下應用之間的跳轉或者微應用跳主應用頁面,直接使用微應用的路由實例是不行的,原因是微應用的路由實例跳轉都基于路由的base。

2.2.1 history.pushState()
2.2.2 將主應用的路由實例通過props傳給微應用,微應用這個路由實例跳轉
// 基座和子應用用的都是一個windows對象,可以在基座中復寫并監聽history.pushState()方法并做相應的跳轉邏輯// 在app.tsx重寫pushState
// 重寫函數// 重寫函數const _wr = function (type: string) {// 拿到windos 的history對象傳過來的參數const org = (window as any).history[type]return function() {// 拿到org后對他重新調用下const rv = org.apply(this, arguments);const e: any = new Event(type)// 返回發布一個自定義事件e.arguments = argumentswindow.dispatchEvent(e)return rv}}// 把重寫的函數賦值給window上pushStatewindow.history.pushState = _wr('pushState')// 在這個函數中做跳轉后的邏輯const bindHistory = () => {const currentPath = window.location.pathname;setSelectedPath(routes.find(item => currentPath.includes(item.key))?.key || '')}// 綁定事件window.addEventListener('pushState', bindHistory)
公共依賴加載

3.1 場景:如果主應用和子應用都使用了相同的庫或者包(antd, axios等),就可以用externals(外部擴展)的方式來引入,減少加載重復報導致資源浪費,就是一個 項目使用后另一個項目不必再重復加載。

3.2.1 主應用:將所有公共依賴配置webpack的externals,并且在index.html使用外鏈引入這些公共依賴
3.2.2 子應用:和主應用一樣配置webpack的externals,并且在index.html使用外鏈引入這些公共依賴,注意,還需要給子應用的公共依賴加上ignore屬性(這是自定義的屬性, 非標準屬性),
qiankun在解析時如果發現ignore屬性就會自動忽略

以axios為例:
基座的配置

// 修改config-overrides.js
const { override, addWebpackExternals } = require('customize-cra')// 這個配置就是通過外鏈的方式去引入這個包
module.exports = override(addWebpackExternals({axios: "axios"})
)// 在publi目錄下的index.html添加外鏈
<!-- 注意:這里的公共依賴的版本必須和子應用一致 -->
<script src="https://unpkg.com/axios@1.1.2/dist/axios.min.js"></script>

子應用的配置

// 在umi-app子應用中
// 修改.umirc.ts配置文件
export default {base: '/sub-umi',npmClient: 'npm',plugins: ['@umijs/plugins/dist/qiankun'],qiankun: {slave: {},},headScripts: [{ // 配置外鏈地址,和設置忽略,qiankun在解析時如果發現ignore屬性就會自動忽略src: 'https://unpkg.com/axios@1.1.2/dist/axios.min.js', ignore: true}]
};
全局狀態管理

一般來說,各個子應用是通過業務來劃分的,不同業務線應該降低耦合度,盡量去避免通信,但是如果涉及到一些公共的狀態或者操作,qiankun也是支持的。

qiankun提供了一個全局的GLobalState來共享數據,基座初始化之后,子應用可以監聽到這個數據的變化,也能提交這個數據

// 基座的配置
// 在src/index.tsx中增加如下代碼
import {  initGlobalState } from 'qiankun'
// 基座初始化
const state = { count: 1 }
const actions = initGlobalState(state);
// 基座項目監聽和修改
actions.onGlobalStateChange((state, prev) => {// state: 變更后的狀態; prev 變更前的狀態console.log(state, prev);
})
actions.setGlobalState(state)
// 子應用的配置
// 在src/index.tsx中增加如下代碼
// 在子應用的mount生命周期監聽
export async function mount(props: any) {console.log('props==', props);// 子項目監聽和修改// 然后在子應用中拿到這兩個函數,然后給他設置一個count:2props.onGlobalStateChange((state,prev) => {// state: 變更后的狀態, prev 變更前的狀態console.log('子應用state===',state,prev)// 一般是將這個state存儲到我們子應用的store,然后在其他組件中去用// 這樣就是實現了一個簡單基座和子應用之間的通信// 同樣在其他子應用中想要用到基座傳過來的狀態也是這樣用的,// 修改的話也是調用這個setGlobalState// 監聽變化也是調用onGlobalStateChange})props.setGlobalState({ count: 2 })render(props)
} 

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62566.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62566.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62566.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

配置中心 選型 : Apollo Vs. Nacos Vs. spring cloud config

為什么我們需要一個微服務配置中心&#xff1f; 首先&#xff0c;我們可以想象下&#xff0c;如果沒有配置中心&#xff0c;我們的項目可能是這樣的&#xff1a;不同環境的配置文件都放在項目里面&#xff0c;部署時可以通過啟動參數來指定使用哪個環境的配置。 這種方式有兩…

HarmonyOS(65) ArkUI FrameNode詳解

Node 1、Node簡介2、FrameNode2.1、創建和刪除節點2.2、對FrameNode的增刪改2.3、 FramNode的查詢功能3、demo源碼4、總結5、參考資料1、Node簡介 在HarmonyOS(63) ArkUI 自定義占位組件NodeContainer介紹了自定義節點復用的原理(閱讀本本篇博文之前,建議先讀讀這個),在No…

詳解RabbitMQ在Ubuntu上的安裝

??????? 目錄 Ubuntu 環境安裝 安裝Erlang 查看Erlang版本 退出命令 ?編輯安裝RabbitMQ 確認安裝結果 安裝RabbitMQ管理界面 啟動服務 查看服務狀態 通過IP:port訪問 添加管理員用戶 給用戶添加權限 再次訪問 Ubuntu 環境安裝 安裝Erlang RabbitMq需要…

vue圖片之放大、縮小、1:1、刷新、左切換、全屏、右切換、左旋咋、右旋轉、x軸翻轉、y軸翻轉

先上效果&#xff0c;代碼在下面 <template><!-- 圖片列表 --><div class"image-list"><img:src"imageSrc"v-for"(imageSrc, index) in images":key"index"click"openImage(index)"error"handleI…

【計算機網絡】實驗12:網際控制報文協議ICMP的應用

實驗12 網際控制報文協議ICMP的應用 一、實驗目的 驗證ping命令和tracert命令的工作原理。 二、實驗環境 Cisco Packet Tracer模擬器 三、實驗過程 1.構建網絡拓撲并進行信息標注&#xff0c;將所需要配置的IP地址寫在對應的主機或者路由器旁邊&#xff0c;如圖1所示。 圖…

迭代器模式的理解和實踐

引言 在軟件開發中&#xff0c;我們經常需要遍歷容器對象&#xff08;如數組、列表、集合等&#xff09;中的元素。如果每個容器對象都實現自己的遍歷算法&#xff0c;那么代碼將會變得冗余且難以維護。為了解決這個問題&#xff0c;迭代器模式應運而生。迭代器模式是一種行為型…

TS2339: Property ‘value‘ does not exist on type ‘MessageBoxData‘.

1、源代碼 <template><el-dialog:visible"visible":before-close"handleClose":close-on-click-modal"false"title"邀請碼"width"1200px"append-to-bodydestroy-on-close><div class"invite-code-wrap…

ubuntu防火墻(三)——firewalld使用與講解

本文是Linux下&#xff0c;用ufw實現端口關閉、流量控制(二) firewalld使用方式 firewalld 是一個動態管理防火墻的工具&#xff0c;主要用于 Linux 系統&#xff08;包括 Ubuntu 和 CentOS 等&#xff09;。它提供了一個基于區域&#xff08;zones&#xff09;和服務&#x…

Windows 安裝配置 RabbitMQ 詳解

博主介紹&#xff1a; 計算機科班人&#xff0c;全棧工程師&#xff0c;掌握C、C#、Java、Python、Android等主流編程語言&#xff0c;同時也熟練掌握mysql、oracle、sqlserver等主流數據庫&#xff0c;能夠為大家提供全方位的技術支持和交流。 工作五年&#xff0c;具有豐富的…

R語言的數據結構--矩陣

【圖書推薦】《R語言醫學數據分析實踐》-CSDN博客 《R語言醫學數據分析實踐 李丹 宋立桓 蔡偉祺 清華大學出版社9787302673484》【摘要 書評 試讀】- 京東圖書 (jd.com) R語言醫學數據分析實踐-R語言的數據結構-CSDN博客 矩陣是一個二維數組&#xff0c;矩陣中的元素都具有相…

JAVA基礎學習筆記_反射+動態代理

文章目錄 反射獲取class對象的三種方式獲取構造方法獲取成員變量獲取成員方法反射的作用 動態代理 反射 允許對成員變量\成員方法\構造方法的信息進行編程訪問 把類內的信息扒的干干凈凈,獲取解剖 獲取從class字節碼文件中獲取 獲取class對象的三種方式 public static void …

微信小程序一鍵復制功能

wx.setClipboardData(Object object) 設置系統剪貼板的內容。調用成功后&#xff0c;會彈出 toast 提示"內容已復制"&#xff0c;持續 1.5s wx.setClipboardData({data: 你需要復制的內容,success (res) {wx.getClipboardData({success (res) {console.log(res.dat…

【Python網絡爬蟲 常見問題匯總】

目錄 1. 爬取圖片出現403解決辦法&#xff1a;設置請求頭中的Referer字段 2.關于干壞事的問題后續不定期更新 歡迎共同探討學習進步 1. 爬取圖片出現403 問題出自案例9&#xff0c;已解決。 【Python網絡爬蟲筆記】9- 抓取優美圖庫高清壁紙 當在爬取圖庫圖片時遇到 403 錯誤…

Linux: docker: 怎么修改 proc下的文件內容?

文章目錄 參考問題方法 1:在宿主機上修改參數方法 2:啟動容器時掛載 /proc 為可寫方法 3:通過 Kubernetes 調整配置方法 4:構建特權容器參考 https://docs.docker.com/security/for-admins/hardened-desktop/enhanced-container-isolation/features-benefits/#procfs–sys…

分布式 分布式事務 總結

前言 相關系列 《分布式 & 目錄》《分布式 & 分布式事務 & 總結》《分布式 & 分布式事務 & 問題》 分布式事務 所謂分布式事務是指操作范圍籠罩多個不同節點的事務。例如對于訂單節點&庫存節點而言&#xff0c;一次完整的交易需要同時調動兩個節…

STM32+模擬或硬件IIC+SHT20驅動問題:接上拉電阻、BUSY死鎖?

主要問題&#xff1a; 1&#xff0c;使用STM32F103C8T6&#xff0c;模擬IIC&#xff0c;SCL和SDA口配置為推挽輸出上拉&#xff0c;主要是SDA腳&#xff0c;每次都要輸出輸入模式重新配置&#xff0c;雖然也能通信&#xff0c;但不穩定&#xff0c;出錯率大&#xff1b; 2&…

【工業機器視覺】基于深度學習的水表盤讀數識別(3-數據標注與轉換)

【工業機器視覺】基于深度學習的儀表盤識讀&#xff08;2&#xff09;-CSDN博客 數據標注 標注擴展 Labelme 和 LabelImg 都是用于創建機器學習和計算機視覺項目所需標注數據的工具。它們都允許用戶通過圖形界面手動標注圖像&#xff0c;但各自有其特點和適用場景。 Labelme…

靜態路由與交換機配置實驗

1.建立網絡拓撲 添加2臺計算機&#xff0c;標簽名為PC0、PC1&#xff1b;添加2臺二層交換機2960&#xff0c;標簽名為S0、S1&#xff1b;添加2臺路由器2811&#xff0c;標簽名為R0、R1&#xff1b;交換機劃分的VLAN及端口根據如下拓撲圖&#xff0c;使用直通線、DCE串口線連接…

【Spark】Spark Join類型及Join實現方式

Spark Join類型 1. Inner Join (內連接) 示例&#xff1a;val result df1.join(df2, df1("id") df2("id"), "inner")執行邏輯&#xff1a;只返回那些在兩個表中都有匹配的行。 2. Left Join (左外連接) 示例&#xff1a;val result df1.jo…

socket UDP 環路回顯的服務端

基于socket通訊的方式&#xff0c;無論用http或者udp或者自定義的協議&#xff0c;程序結構都是類似的。這個以UDP協議為例簡要說明。 #include <stdio.h> // 標準輸入輸出庫 #include <sys/types.h> // 提供了一些數據類型&#xff0c;如ssize_t #include <sy…