用中聲明式接入微應(yīng)用:qiankun `<MicroApp>` 組件與 `MicroAppLink` 完整指南)
在 Vue 主應(yīng)用中聲明式接入微應(yīng)用qiankunMicroApp組件與MicroAppLink完整指南【免費(fèi)下載鏈接】qiankun Blazing fast, simple and complete solution for micro frontends.項(xiàng)目地址: https://gitcode.com/gh_mirrors/qi/qiankunqiankunjs/vue是 qiankun 官方提供的 Vue 綁定包通過MicroApp組件以聲明式方式加載、掛載、更新與卸載微應(yīng)用將微應(yīng)用實(shí)例的生命周期與 Vue 組件的生命周期綁定在一起同時(shí)提供MicroAppLink用于 Vue 3 主應(yīng)用的路由導(dǎo)航。本文以 docs/zh-CN/ecosystem/vue.md 為骨架結(jié)合倉庫內(nèi) Vue 綁定源碼 與 共享邏輯完整講解安裝方式、全部 Props 與插槽、加載/錯(cuò)誤界面、props 深度更新、實(shí)例句柄與 CSS 鉤子幫助你用最少的樣板代碼把微應(yīng)用嵌入 Vue 主應(yīng)用并理解組件底層與loadMicroApp的封裝關(guān)系。安裝npm install qiankunjs/vuerc qiankunrc主應(yīng)用必須安裝vue版本范圍為^2.0.0 || 3.0.0。組件基于vue-demi構(gòu)建同一份構(gòu)建產(chǎn)物同時(shí)支持 Vue 2 和 Vue 3因此Vue 2 項(xiàng)目還需要額外安裝vue/composition-api組件通過vue-demi使用組合式 API。從 packages/ui-bindings/vue/package.json 可以看到完整的依賴聲明vue-demi^0.14.10作為運(yùn)行時(shí)依賴負(fù)責(zé) Vue 2/3 運(yùn)行時(shí)切換vue與qiankun^3.0.0-rc.15作為 peer dependencyvue/composition-api^1.7.2標(biāo)記為可選 peer dependency僅在 Vue 2 下需要。包同時(shí)提供 CJS 與 ESM 兩種構(gòu)建產(chǎn)物main指向dist/cjs、module指向dist/esm類型聲明隨dist/esm/index.d.ts一起發(fā)布。::: tip 使用前提MicroApp組件內(nèi)部直接調(diào)用loadMicroApp單獨(dú)使用時(shí)無需調(diào)用registerMicroApps或start。如果同一主應(yīng)用里還使用基于路由的注冊(cè)方式則仍需調(diào)用start。掛載和更新操作與 single-spa 生命周期的對(duì)應(yīng)關(guān)系參見微應(yīng)用生命周期與 props。 :::路由導(dǎo)航MicroAppLinkMicroAppLink用于 Vue 3 主應(yīng)用的registerMicroApps路由模式。主應(yīng)用完成注冊(cè)并調(diào)用start后點(diǎn)擊鏈接即可通過 single-spa 的navigateToUrl切換 URL由已注冊(cè)的activeRule決定微應(yīng)用的掛載與卸載。鏈接本身不加載微應(yīng)用它只是路由導(dǎo)航的聲明式入口。script setup langts import { MicroAppLink, type MicroAppLinkProps } from qiankunjs/vue; const appLink: MicroAppLinkProps { to: /app1, className: nav-link, activeClassName: is-active, }; /script template nav MicroAppLink v-bindappLink應(yīng)用一/MicroAppLink MicroAppLink to/app2/settings replace應(yīng)用二設(shè)置/MicroAppLink /nav /templateMicroAppLink 屬性屬性類型說明tostring必填。目標(biāo) URL作為鏈接的href。replaceboolean是否替換當(dāng)前歷史記錄。默認(rèn)值為false導(dǎo)航時(shí)新增一條歷史記錄。classNamestring鏈接的 CSS 類名模板中也可寫為class-name。activeClassNamestring當(dāng)前地址匹配目標(biāo)地址前綴時(shí)追加的 CSS 類名模板中也可寫為active-class-name。默認(rèn)不追加。默認(rèn)插槽提供鏈接內(nèi)容。MicroAppLinkProps從包入口導(dǎo)出見 packages/ui-bindings/vue/src/index.ts。除組件自身使用的屬性外class、target、rel、download、aria-*、data-*和事件監(jiān)聽器等原生鏈接屬性都會(huì)傳遞給ahref由to指定。點(diǎn)擊攔截與導(dǎo)航規(guī)則從源碼 packages/ui-bindings/vue/src/MicroAppLink.ts 可以看到組件的onClick會(huì)先執(zhí)行傳入的click監(jiān)聽器再判斷是否接管導(dǎo)航。其核心判斷邏輯位于共享模塊 packages/ui-bindings/shared/src/link.ts 的navigateMicroAppLink函數(shù)只有同時(shí)滿足以下條件的左鍵點(diǎn)擊才會(huì)阻止默認(rèn)行為并在當(dāng)前頁面內(nèi)導(dǎo)航事件未被preventDefault()取消未按下 Ctrl / Meta / Shift / Alt 修飾鍵目標(biāo)為當(dāng)前窗口有效的target為空或?yàn)開self未設(shè)置時(shí)遵循頁面的base target設(shè)置鏈接為同源的 HTTP(S) 鏈接且不帶download屬性。外鏈、下載鏈接、其他窗口目標(biāo)及帶修飾鍵的點(diǎn)擊均保留瀏覽器行為??赏ㄟ^click.prevent取消組件導(dǎo)航。replace為true時(shí)組件使用history.replaceState替換當(dāng)前記錄并通過popstate事件通知路由監(jiān)聽器源碼中用一個(gè)臨時(shí)的事件監(jiān)聽對(duì)象探測(cè) single-spa 是否已同步處理該通知避免重復(fù)派發(fā)popstate。activeClassName 的前綴匹配activeClassName的匹配邏輯在isMicroAppLinkActive函數(shù)中實(shí)現(xiàn)將目標(biāo) URL 解析后以它的pathname search hash為前綴匹配當(dāng)前地址的對(duì)應(yīng)部分。例如to/app1會(huì)匹配/app1/settings也會(huì)匹配/app10to/會(huì)匹配所有路徑查詢參數(shù)和哈希若包含在to中也參與前綴匹配。這是字符串前綴匹配不解析路由參數(shù)需要精確匹配時(shí)可由主應(yīng)用自行設(shè)置className和aria-current。此外組件在掛載時(shí)會(huì)通過subscribeToMicroAppLinkLocation訂閱popstate、hashchange和single-spa:routing-event事件確保瀏覽器前進(jìn)后退或 single-spa 重路由后activeClassName依然保持最新。基本用法script setup import { MicroApp } from qiankunjs/vue; /script template micro-app nameapp1 entryhttp://localhost:8000 / /templatename和entry是僅有的兩個(gè)必填 prop。name用于標(biāo)識(shí)當(dāng)前實(shí)例entry用于指定微應(yīng)用的 HTML 入口 URL。缺少其中任意一項(xiàng)時(shí)組件僅輸出錯(cuò)誤日志共享邏輯mountMicroApp中的the name and entry of MicroApp is needed不會(huì)加載微應(yīng)用也不會(huì)拋出異常。組件會(huì)渲染一個(gè)class為qiankun-micro-app-container的div并將微應(yīng)用內(nèi)容流式寫入該容器。只有啟用加載狀態(tài)或錯(cuò)誤邊界時(shí)組件才會(huì)額外渲染一層包裹元素詳見下文加載與錯(cuò)誤界面。從 MicroApp.ts 的render函數(shù)可以確認(rèn)一個(gè)實(shí)現(xiàn)細(xì)節(jié)掛載容器div會(huì)先于插槽渲染。這是有意的設(shè)計(jì)——qiankun 以容器的 XPath 作為 Parcel 緩存鍵如果加載指示器條件渲染導(dǎo)致容器在 DOM 樹中的兄弟序號(hào)變化會(huì)把同一個(gè)微應(yīng)用拆散到兩個(gè)緩存鍵下。PropsProp類型默認(rèn)值說明namestring—必填。微應(yīng)用實(shí)例的名稱值發(fā)生變化時(shí)會(huì)重新掛載。entrystring—必填。微應(yīng)用的 HTML 入口 URL。settingsAppConfiguration{ sandbox: true }傳遞給loadMicroApp的加載器和沙箱配置。參見 AppConfiguration。lifeCyclesLifeCyclesundefined由主應(yīng)用提供的生命周期鉤子包括beforeLoad、beforeMount、afterMount、beforeUnmount和afterUnmount。每項(xiàng)可傳入函數(shù)或函數(shù)數(shù)組。參見生命周期鉤子。autoSetLoadingbooleanfalse微應(yīng)用加載期間渲染內(nèi)置的加載指示器。autoCaptureErrorbooleanfalse加載失敗時(shí)渲染內(nèi)置的錯(cuò)誤邊界。wrapperClassNamestringundefined包裹元素上的額外 CSS 類名。僅在啟用加載狀態(tài)或錯(cuò)誤邊界時(shí)生效。classNamestringundefined掛載容器元素上的額外 CSS 類名。appPropsobjectundefined傳遞給微應(yīng)用的 props。Vue 綁定僅通過該屬性向微應(yīng)用傳遞數(shù)據(jù)。::: infosettings的默認(rèn)值與 React 綁定不同 Vue 綁定的settings默認(rèn)值為{ sandbox: true }React 綁定則不設(shè)置默認(rèn)值不會(huì)替你填任何默認(rèn)項(xiàng)。兩者的sandbox在 qiankun 核心運(yùn)行時(shí)中默認(rèn)值都為true因此不額外傳值時(shí)行為一致。 :::::: warning 業(yè)務(wù)數(shù)據(jù)通過appProps傳遞 React 綁定會(huì)將MicroApp上的附加 prop 傳遞給微應(yīng)用Vue 綁定則不會(huì)傳遞任意附加屬性。業(yè)務(wù)數(shù)據(jù)必須放入appProps對(duì)象未聲明的其他屬性會(huì)被忽略。當(dāng)前實(shí)現(xiàn)還會(huì)將autoSetLoading、autoCaptureError和appProps對(duì)象本身傳入微應(yīng)用業(yè)務(wù)代碼不應(yīng)依賴這些組件控制字段。 :::settings、lifeCycles等組件自身消費(fèi)的屬性在共享層被統(tǒng)一定義在componentOwnedProps列表中見 packages/ui-bindings/shared/src/index.tsomitSharedProps會(huì)把這些字段從最終傳給微應(yīng)用的 props 中剔除避免加載指示器插槽等渲染閉包泄漏進(jìn)子應(yīng)用。settingsAppConfigurationsettings與loadMicroApp的第二個(gè)參數(shù)結(jié)構(gòu)相同。完整定義參見 AppConfiguration字段為fetch、streamTransformer、nodeTransformer和sandbox默認(rèn)值為true。樣式隔離、額外全局變量、孵化上下文和隔離插件均位于sandbox對(duì)象內(nèi)部。template micro-app nameapp1 entryhttp://localhost:8000 :settings{ sandbox: { styleIsolation: true } } / /template如需為特定微應(yīng)用關(guān)閉 JavaScript 沙箱可傳入:settings{ sandbox: false }。相關(guān)行為參見 JavaScript 隔離和樣式隔離。在共享層 mountMicroApp 的實(shí)現(xiàn)中settings會(huì)原樣合并進(jìn)loadMicroApp的configuration參數(shù)lifeCycles則作為第三個(gè)參數(shù)直接透?jìng)鳌创a注釋特別指出歷史上曾用concat(undefined, hook)包裝每個(gè)鉤子結(jié)果 qiankun 把[undefined, hook]當(dāng)作鉤子調(diào)用導(dǎo)致所有傳入lifeCycles的應(yīng)用掛載即失敗因此現(xiàn)在改為原樣透?jìng)?。向微?yīng)用傳遞 propsappProps需要傳遞給微應(yīng)用的數(shù)據(jù)應(yīng)放入appPropsscript setup import { reactive } from vue; import { MicroApp } from qiankunjs/vue; const appProps reactive({ userId: 42, theme: dark }); /script template micro-app nameapp1 entryhttp://localhost:8000 :appPropsappProps / /template這些數(shù)據(jù)會(huì)作為props參數(shù)傳給微應(yīng)用導(dǎo)出的生命周期函數(shù)// 微應(yīng)用內(nèi)部 export async function mount(props) { console.log(props.userId); // 42 }組件會(huì)深度偵聽appProps源碼中watch(appProps, ..., { deep: true })。修改嵌套值例如appProps.theme light會(huì)嘗試更新當(dāng)前實(shí)例。執(zhí)行更新前微應(yīng)用必須導(dǎo)出update生命周期、處于MOUNTED狀態(tài)并且尚未開始卸載。相關(guān)說明參見應(yīng)用間共享狀態(tài)與通信。::: tipupdate僅在掛載完成后執(zhí)行 共享層updateMicroApp會(huì)等待mountPromise完成再按順序處理更新并且僅在 Parcel 狀態(tài)為MOUNTED時(shí)調(diào)用update。掛載期間發(fā)生的中間狀態(tài)變化不保證逐次觸發(fā)更新。 :::更新隊(duì)列與防抖提示源碼層面updateMicroApp通過_updatingPromise把每次更新串成一條鏈保證后一個(gè)更新必須等待前一個(gè)更新完成與組件狀態(tài)變更順序一致。首次更新以mountPromise為鏈的起點(diǎn)——注釋明確指出這里只能補(bǔ)上起點(diǎn)而不能跳過本次更新否則宿主傳入的第一次 props 變更會(huì)被直接吞掉。另外在開發(fā)環(huán)境下NODE_ENV development如果同一微應(yīng)用在200ms 內(nèi)更新次數(shù)過多updateMicroApp會(huì)打印一條優(yōu)化提示警告[qiankunjs/ui-shared] It seems like microApp app1 is updating too many times in a short time(200ms)...這通常意味著主應(yīng)用在頻繁的響應(yīng)式重渲染中反復(fù)改動(dòng)了appProps值得檢查是否需要節(jié)流或解耦數(shù)據(jù)更新頻率。加載與錯(cuò)誤界面加載指示器和錯(cuò)誤邊界均需顯式啟用。如果未啟用這兩項(xiàng)功能也未提供對(duì)應(yīng)插槽組件只渲染掛載容器div。如果設(shè)置了autoSetLoading、autoCaptureError、#loader插槽或#error-boundary插槽中的任意一項(xiàng)組件會(huì)額外渲染class為qiankun-micro-app-wrapper的包裹元素用于容納加載節(jié)點(diǎn)、錯(cuò)誤節(jié)點(diǎn)和掛載容器。對(duì)應(yīng)源碼中mountMicroApp在開始加載時(shí)調(diào)用setLoading(true)在mountPromise成功或loadPromise/bootstrapPromise任一失敗時(shí)調(diào)用setLoading(false)錯(cuò)誤則通過setError交給組件的setComponentError統(tǒng)一處理。自動(dòng)加載與錯(cuò)誤捕獲通過以下兩個(gè)布爾 prop 啟用內(nèi)置指示器script setup import { MicroApp } from qiankunjs/vue; /script template micro-app nameapp1 entryhttp://localhost:8000 autoSetLoading autoCaptureError / /template內(nèi)置界面僅提供基礎(chǔ)占位內(nèi)容默認(rèn)加載界面MicroAppLoader.ts渲染文本loading...默認(rèn)錯(cuò)誤邊界ErrorBoundary.ts渲染包含error.message的div。生產(chǎn)環(huán)境通常應(yīng)使用下文介紹的插槽提供自定義界面。::: info 加載狀態(tài)的初始值 Vue 綁定將loading初始化為falseReact 綁定的初始值則為true。微應(yīng)用開始加載時(shí)該狀態(tài)會(huì)設(shè)為true啟用autoSetLoading后組件會(huì)在mountPromise完成時(shí)將其恢復(fù)為false。未啟用autoSetLoading時(shí)組件不會(huì)渲染內(nèi)置加載界面。 :::自定義loader插槽可通過#loader作用域插槽渲染自定義加載指示器。組件會(huì)將布爾值loading直接傳給插槽加載期間為true加載結(jié)束后為false。script setup import CustomLoader from /components/CustomLoader.vue; import { MicroApp } from qiankunjs/vue; /script template micro-app nameapp1 entryhttp://localhost:8000 autoSetLoading template #loaderloading custom-loader :loadingloading / /template /micro-app /template#loader插槽的優(yōu)先級(jí)高于內(nèi)置加載界面。提供該插槽后組件不會(huì)渲染默認(rèn)加載界面仍需設(shè)置autoSetLoading組件才會(huì)在mountPromise完成后自動(dòng)將loading設(shè)為false。自定義錯(cuò)誤邊界插槽可通過#error-boundary作用域插槽渲染自定義錯(cuò)誤界面。組件會(huì)將Error實(shí)例直接傳給插槽。該插槽僅在發(fā)生錯(cuò)誤后渲染。script setup import CustomErrorBoundary from /components/CustomErrorBoundary.vue; import { MicroApp } from qiankunjs/vue; /script template micro-app nameapp1 entryhttp://localhost:8000 template #error-boundaryerror custom-error-boundary :errorerror / /template /micro-app /template未捕獲的錯(cuò)誤會(huì)重新拋出如果既未啟用autoCaptureError也未提供#error-boundary插槽則load、bootstrap和mount階段的錯(cuò)誤會(huì)從異步加載流程中重新拋出。建議啟用組件內(nèi)置錯(cuò)誤界面或提供自定義錯(cuò)誤界面避免產(chǎn)生未處理的 Promise 拒絕。::: warning 啟用autoCaptureError或提供#error-boundary插槽后組件會(huì)通過錯(cuò)誤界面呈現(xiàn)異常不再重新拋出。同一微應(yīng)用應(yīng)選擇一種錯(cuò)誤處理方式不要同時(shí)依賴外層errorCaptured與組件內(nèi)錯(cuò)誤邊界。詳見處理微應(yīng)用錯(cuò)誤。 :::一個(gè)值得注意的源碼細(xì)節(jié)為了兼容文檔中一直使用的#error-boundary寫法組件在讀取插槽時(shí)會(huì)同時(shí)接受errorBoundary與error-boundary兩種拼寫見 MicroApp.ts 中slots.errorBoundary ?? slots[error-boundary]因?yàn)?Vue 不會(huì)像規(guī)范化 prop 名那樣規(guī)范化插槽名。重新掛載與實(shí)例句柄組件僅偵聽name的變化來觸發(fā)重新掛載。修改該 prop 會(huì)卸載當(dāng)前微應(yīng)用并創(chuàng)建新實(shí)例僅修改entry、settings或lifeCycles不會(huì)創(chuàng)建新實(shí)例。組件銷毀時(shí)onBeforeUnmount會(huì)自動(dòng)卸載微應(yīng)用卸載操作會(huì)等待正在進(jìn)行的mountPromise完成以保持掛載和卸載的執(zhí)行順序。從源碼看掛載/卸載通過一條lifecyclePromise 鏈串行化unmount().then(() mountMicroApp(...))。源碼注釋解釋了這樣做的原因mountMicroApp在應(yīng)用交接后還要再過一個(gè) tick 才 resolve如果用戶在第一次切換尚未落定前又觸發(fā)了一次name變更沒有這條鏈就可能把兩個(gè)微應(yīng)用競(jìng)態(tài)地掛進(jìn)同一個(gè)容器——這正是router-view在用戶快速連點(diǎn)時(shí)的典型場(chǎng)景。此外name的 watcher 在觸發(fā)時(shí)會(huì)立即快照當(dāng)時(shí)的 props{ ...originProps, ...appProps.value }避免排隊(duì)的掛載執(zhí)行時(shí)讀到更晚一次切換的 props導(dǎo)致最后一個(gè)應(yīng)用被反復(fù)掛載又拆掉。可以通過組件實(shí)例的microApp和microAppRef兩個(gè)屬性訪問當(dāng)前微應(yīng)用實(shí)例兩者均指向同一個(gè)MicroAppParcel 句柄??赏ㄟ^模板 ref 訪問該句柄script setup import { ref, onMounted } from vue; import { MicroApp } from qiankunjs/vue; const microAppComp ref(); onMounted(() { // Parcel 句柄getStatus()、mountPromise、unmount()、update() 等 console.log(microAppComp.value?.microApp?.getStatus()); }); /script template micro-app refmicroAppComp nameapp1 entryhttp://localhost:8000 / /template該句柄是qiankunjs/single-spaqiankun 內(nèi)置的 single-spa fork的 Parcel。getStatus()返回NOT_LOADED、LOADING_SOURCE_CODE、NOT_BOOTSTRAPPED、BOOTSTRAPPING、NOT_MOUNTED、MOUNTING、MOUNTED、UPDATING、UNMOUNTING、UNLOADING、SKIP_BECAUSE_BROKEN或LOAD_ERROR。完整類型參見類型參考。::: tip 由組件管理生命周期 應(yīng)優(yōu)先通過 propname、appProps管理微應(yīng)用而不是直接調(diào)用句柄上的unmount()或update()。組件會(huì)按順序執(zhí)行卸載并協(xié)調(diào)并發(fā)更新直接調(diào)用句柄方法可能與組件的內(nèi)部狀態(tài)發(fā)生沖突。 :::CSS 鉤子CSS 類名與 React 綁定一致。組件會(huì)添加兩個(gè)穩(wěn)定的類名并在提供wrapperClassName或className時(shí)將自定義值添加到對(duì)應(yīng)類名之前。元素始終應(yīng)用的類名prop 提供的額外類名包裹元素僅在啟用加載狀態(tài)或錯(cuò)誤邊界時(shí)存在qiankun-micro-app-wrapperwrapperClassName掛載容器qiankun-micro-app-containerclassName/* 所有微應(yīng)用的掛載容器 */ .qiankun-micro-app-container { min-height: 320px; } /* 承載加載界面和錯(cuò)誤界面的包裹元素 */ .qiankun-micro-app-wrapper { position: relative; }包裹元素僅在啟用加載狀態(tài)或錯(cuò)誤邊界時(shí)存在。因此如果micro-app未配置加載或錯(cuò)誤界面wrapperClassName不會(huì)產(chǎn)生效果。完整示例以下示例組合了本文介紹的全部核心能力appProps響應(yīng)式傳參、樣式隔離、內(nèi)置加載狀態(tài)、自定義加載與錯(cuò)誤插槽、以及自定義 CSS 類名script setup import { reactive } from vue; import { MicroApp } from qiankunjs/vue; import Spinner from /components/Spinner.vue; import ErrorPanel from /components/ErrorPanel.vue; const appProps reactive({ userId: 42 }); /script template micro-app nameapp1 entryhttp://localhost:8000 :settings{ sandbox: { styleIsolation: true } } :appPropsappProps autoSetLoading wrapperClassNamemy-wrapper classNamemy-container template #loaderloading spinner v-ifloading / /template template #error-boundaryerror error-panel :messageerror.message / /template /micro-app /template組件與 loadMicroApp 的封裝關(guān)系如果希望脫離組件直接操作實(shí)例可閱讀loadMicroApp的文檔與 核心實(shí)現(xiàn)。組件本質(zhì)上是它的薄封裝name、entry對(duì)應(yīng)LoadableApp的name、entry容器由組件內(nèi)部管理模板 ref 拿到qiankun-micro-app-container的divsettings對(duì)應(yīng)第二個(gè)參數(shù)AppConfiguration其中sandbox是隔離能力的統(tǒng)一入口對(duì)象形式可承載styleIsolation、globals、incubatorContext、plugins等lifeCycles對(duì)應(yīng)第三個(gè)參數(shù)LifeCyclesappProps經(jīng)omitSharedProps過濾后作為props傳給微應(yīng)用生命周期。這種封裝讓你既能享受聲明式組件的便利生命周期自動(dòng)管理、深度偵聽更新、加載/錯(cuò)誤界面又保留了loadMicroApp全部配置能力需要精細(xì)控制時(shí)仍可通過microApp句柄拿到 Parcel 級(jí)別的狀態(tài)與 Promise。相關(guān)內(nèi)容ReactMicroApp組件——React 綁定及其 prop 傳遞方式。loadMicroApp——組件所封裝的核心 API。AppConfiguration——settings的類型定義。微應(yīng)用生命周期與 props——mount、update和unmount的語義。運(yùn)行多個(gè)微應(yīng)用實(shí)例——同時(shí)掛載多個(gè)微應(yīng)用?!久赓M(fèi)下載鏈接】qiankun Blazing fast, simple and complete solution for micro frontends.項(xiàng)目地址: https://gitcode.com/gh_mirrors/qi/qiankun創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考