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

          基于 qiankun 的微前端實(shí)踐

          共 10353字,需瀏覽 21分鐘

           ·

          2022-07-11 14:01

          ?

          術(shù)  堅(jiān)  

          前言

          微前端(Micro-Frontends)是一種類(lèi)似于微服務(wù)的架構(gòu),它將微服務(wù)的理念應(yīng)用于瀏覽器端,即將 Web 應(yīng)用由單一的單體應(yīng)用轉(zhuǎn)變?yōu)槎鄠€(gè)小型前端應(yīng)用聚合為一的應(yīng)用。

          微前端并不是前端領(lǐng)域的新概念。早期希望前端工程能夠像后臺(tái)的微服務(wù)一樣,項(xiàng)目分開(kāi)自治,核心的訴求是:

          1、兼容不同技術(shù)棧

          2、將項(xiàng)目看作頁(yè)面、組件,能夠復(fù)用到不同的系統(tǒng)中

          早期比較成熟的 single-spa,從早期 React 的現(xiàn)代框架組件生命周期中獲得靈感,將生命周期應(yīng)用于這個(gè)應(yīng)用程序,即將整個(gè)頁(yè)面作為組件。

          后來(lái)螞蟻金融團(tuán)隊(duì)孵化了基于 single-spa 的 qiankun 架構(gòu),將微前端進(jìn)一步的深耕,目標(biāo)直指巨石應(yīng)用業(yè)務(wù)難題,旨在解決單體應(yīng)用在一個(gè)相對(duì)長(zhǎng)的時(shí)間跨度下,由于參與的人員、團(tuán)隊(duì)的增多、變遷,從一個(gè)普通應(yīng)用演變成一個(gè)巨石應(yīng)用 (Frontend Monolith) 后,隨之而來(lái)的應(yīng)用不可維護(hù)的問(wèn)題。這類(lèi)問(wèn)題在企業(yè)級(jí) Web 應(yīng)用中尤其常見(jiàn)。

          本人在深入實(shí)踐微前端之后,深感 qiankun 受制于前端架構(gòu)的定位,無(wú)法使用 Nodejs 等能力快速解決快速發(fā)布,構(gòu)建,管理的困境,因此在此基礎(chǔ)上做了一定程度的 APAAS 探索,將本文的項(xiàng)目作為 APAAS 應(yīng)用快速集成到其他業(yè)務(wù)系統(tǒng)。

          qiankun 介紹

          首先基于作者自己的思考,給大家梳理下 qiankun 微前端的渲染流程,方便不了解微前端的同學(xué)有個(gè)大體的認(rèn)識(shí)。

          假如你不了解微前端的話,對(duì)于這個(gè)新事物的學(xué)習(xí)和探索,一般是按照 5Ws 的學(xué)習(xí)規(guī)律來(lái)學(xué)習(xí):

          1. Who is it about?

          2. What happened?

          3. When did it take place?

          4. Where did it take place?

          5. Why did it happen?

          本文也按照類(lèi)似的方式一個(gè)具體的案例去梳理 qiankun 的核心概念。

          本文有一個(gè)業(yè)務(wù)系統(tǒng) A,希望集成業(yè)務(wù)系統(tǒng) B 的歡迎卡片配置功能頁(yè)面。

          那么 who 包含 qiankun 客戶端、主應(yīng)用 A、微應(yīng)用 B 三部分,三者協(xié)作完成了微前端的設(shè)計(jì)。從產(chǎn)品使用的角度來(lái)看,第一步則是注冊(cè)路由,確定微前端啟動(dòng)的時(shí)機(jī)和啟動(dòng)渲染的內(nèi)容(when 和 where)。

          import { registerMicroApps, start } from'qiankun';

          registerMicroApps([
          {
          name: 'smart-contact-cms-index', // app name registered
          entry: '//localhost:7100',
          container: '#yourContainer',
          activeRule: '/yourActiveRule',
          },
          ]);

          start();

          上面的實(shí)例十分簡(jiǎn)單,通過(guò) name 來(lái)作為微應(yīng)用 B 的唯一標(biāo)識(shí), entry 作為微應(yīng)用 B 的資源加載地址。當(dāng)路由映射到 '/yourActiveRule' 時(shí),則請(qǐng)求微應(yīng)用 B 的頁(yè)面資源地址,解析其中的 JS 和 CSS 資源,將微應(yīng)用 B 的頁(yè)面渲染到 id 為 #yourContainer 的 DOM 節(jié)點(diǎn)。

          const packageName = require('./package.json').name;

          module.exports = {
          output: {
          library: `${packageName}-[name]`,
          libraryTarget: 'umd',
          jsonpFunction: `webpackJsonp_${packageName}`,
          },
          };

          上面是一段微應(yīng)用 webpack 的配置,library 就是注冊(cè)路由時(shí)的唯一標(biāo)識(shí) name 了。這里也十分好理解為啥 qiankun 要在構(gòu)建資源時(shí)給 js 增加標(biāo)識(shí)。

          qiankun 客戶端 entry 配置的 HTML 頁(yè)面資源的地址,拉取到頁(yè)面資源之后劫持 HTML 解析,自行構(gòu)建 Http 請(qǐng)求去加載 JS 文件。很明顯頁(yè)面中肯定不止一段 JS 代碼,就需要標(biāo)識(shí)來(lái)標(biāo)識(shí)那一段的入口 JS 代碼了。

          下面截取了 index.js 頭部代碼

          !function(e,t){
          "object"==typeofexports&&"object"==typeofmodule?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeofexports?exports["smart-contact-cms-index"]=t():e["smart-contact-cms-index"]=t()
          }

          僅僅修改微應(yīng)用 B 的 webpack 是遠(yuǎn)遠(yuǎn)不夠的,常規(guī)前端頁(yè)面工程會(huì)在 index.js 文件中直接調(diào)用類(lèi)似 ReactDOM.render API,直接去渲染到特定節(jié)點(diǎn)。那 qiankun 根本無(wú)法干預(yù)渲染過(guò)程,因此需要將渲染的實(shí)際交給 qiankun 客戶端來(lái)控制,qiankun 受前端組件化的影響,也希望將頁(yè)面做成一個(gè)暴露生命周期的組件,導(dǎo)出相關(guān)的生命周期鉤子。

          // 單獨(dú) App 運(yùn)行
          if (!isMicroApp) {
          // 非微前端渲染
          renderApp({});
          }

          // 導(dǎo)出 qiankun 生命周期
          export const bootstrap = async () => {
          console.log('[smart-contact-cms] bootstrap');
          };

          export const mount = async (props: any) => {
          console.log('[smart-contact-cms] mount', props);
          normalizeObject(props.mainAppState);
          // 作為微應(yīng)用運(yùn)行
          renderApp(props);
          };

          export const unmount = async (props: any) => {
          const { container } = props;
          ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
          };

          細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)有個(gè) isMicroApp 的變量可以控制啟動(dòng)微前端渲染還是獨(dú)立 APP 渲染,這個(gè)實(shí)際是 qiankun 客戶端啟動(dòng)后,會(huì)在 window 上掛在 __POWERED_BY_QIANKUN__ 變量標(biāo)識(shí)運(yùn)行時(shí)環(huán)境。

          這里還有個(gè)小細(xì)節(jié)就是微應(yīng)用的 JS CSS 文件請(qǐng)求是屬于 Ftech/XHR 類(lèi)型,說(shuō)明 js 文件的請(qǐng)求是 qiankun 客戶端自行構(gòu)造的。

          至于為何要這么做?我的理解是為了實(shí)現(xiàn)微前端的沙箱功能。

          qiankun 客戶端干預(yù) script 標(biāo)簽和 link 標(biāo)簽的加載過(guò)程的難度,和上面的方式相比明顯是更簡(jiǎn)單的,但是這種方式很明顯是缺乏安全性,特別是在面臨第三方 SDK 加載的時(shí)候,例如微信 web SDK 的引入,就無(wú)法通過(guò)其安全校驗(yàn),具體可以詳細(xì)看下 FAQ 1 的案例介紹。

          一句話總結(jié)上述過(guò)程:

          在 qiankun 的框架下,一個(gè)頁(yè)面集成到另外一個(gè)頁(yè)面系統(tǒng)中,最關(guān)鍵的核心點(diǎn)就是將微應(yīng)用封裝成具有生命周期的頁(yè)面組件,使得 qiankun 可以調(diào)用 React 或者 Vue 的 render 能力,將頁(yè)面渲染到對(duì)應(yīng)的 DOM 節(jié)點(diǎn)。

          APAAS 架構(gòu)介紹

          本文由于篇幅限制,只介紹 Client 端、Server 端的接口協(xié)議代理、微應(yīng)用改造,其他部分更多是偏向于自動(dòng)工程化和項(xiàng)目管理的方面,之后有時(shí)間給大家詳細(xì)介紹下。

          Client 端

          Client 端是經(jīng)過(guò)業(yè)務(wù)封裝后的 qiankun SDK,內(nèi)部集成了經(jīng)過(guò) qiankun 改造的各自子應(yīng)用系統(tǒng),對(duì)外暴露以下接口:

          1、initial(appInfo)

          appInfo 參數(shù)

          {
          "app_id": "xxx",
          "prefetch": true,
          "signUrl": "remote-url"
          }

          這個(gè)接口主要是為了初始化部分信息,包括微應(yīng)用的鑒權(quán)配置、是否啟動(dòng)預(yù)加載、微應(yīng)用的標(biāo)識(shí)。其中鑒權(quán)配置可能會(huì)讓大家感到疑惑。

          在介紹 qiankun 的渲染過(guò)程中提到

          這里還有個(gè)小細(xì)節(jié)就是微應(yīng)用的 JS CSS 文件請(qǐng)求是屬于 Ftech/XHR 類(lèi)型,說(shuō)明 js 文件的請(qǐng)求是 qiankun 客戶端自行構(gòu)造的。

          這里必然要涉及前端的跨域問(wèn)題,尤其是當(dāng)主應(yīng)用和微應(yīng)用的域名不一致時(shí),qiankun 客戶端如何能夠在跨域的限制之下獲取到微應(yīng)用的頁(yè)面資源?本文的解決方案是主應(yīng)用提供一個(gè)鑒權(quán)秘鑰下發(fā)的接口 signUrl,這個(gè)接口由微應(yīng)用提供也可以,將秘鑰信息下發(fā)到 cookie 中,通過(guò)配置 qiankin 自定義 fetch 方法,帶上這些鑒權(quán)信息。

          本文只是其中一個(gè)解決方案,官方也提供了一些通用方案以供參考《微應(yīng)用靜態(tài)資源一定要支持跨域嗎》。

          2、load (FloadConfig) 接口

          動(dòng)態(tài)加載微應(yīng)用,其 FloadConfig 參數(shù)如下:

          interface FLoadConfig {
          container: string;
          pageId: string;
          props?: FMicroProps; // 傳入對(duì)應(yīng)微應(yīng)用所需要的參數(shù)
          }
          interface FMicroProps {
          loginUrl?: string, // 登錄的重定向地址
          baseApiUrl?: string,
          dispatch?: (value: { type: string; data: any }) =>void,
          pageInfo?: FPageConfig,
          useNativeRoute?: number;
          extra?: any,
          }

          loginUrl 是鑒權(quán)失敗跳轉(zhuǎn)的登錄地址;baseApiUrl 是后臺(tái)請(qǐng)求的地址;useNativeRoute 決定微前端采用何種路由方式加載。

          本文在這個(gè)階段主要做兩方面的突破:

          1. 解決了后臺(tái)請(qǐng)求的跨域和鑒權(quán)

          2. 解決了主應(yīng)用和子應(yīng)用的 path 沖突問(wèn)題

          baseApiUrl 這里默認(rèn)提供了基于騰訊云的鑒權(quán)下發(fā)能力,各個(gè)業(yè)務(wù)系統(tǒng)只需要按照規(guī)范去對(duì)接騰訊云 API 即可,比較遠(yuǎn)離前端,在這里不做過(guò)多解讀。

          在實(shí)際的業(yè)務(wù)場(chǎng)景中,主應(yīng)用和微應(yīng)用互相無(wú)法感知到對(duì)方,因此其路由有可能會(huì)互相沖突,這里通過(guò) useNativeRoute 參數(shù)來(lái)控制微應(yīng)用的路由模式。

          1、useNativeRoute = 0 采用配置端數(shù)據(jù)返回的路由路徑
          2、useNativeRoute = 1 采用當(dāng)前頁(yè)面 hash 前綴 + 配置端數(shù)據(jù)返回的路由路徑作為新路由
          3、useNativeRoute = 3 采用當(dāng)前的頁(yè)面路由不做任何改動(dòng), 默認(rèn) 0

          底層的實(shí)現(xiàn)邏輯則是在 renderApp 傳入主應(yīng)用的路由前綴,改造相對(duì)比較簡(jiǎn)單,但是很實(shí)用。

          exportconst mount = async (props: any) => {
          console.log('[smart-contact-cms] mount', props);

          // 刪除不能序列化的內(nèi)容
          normalizeObject(props.mainAppState);

          // 作為微應(yīng)用運(yùn)行
          renderApp(props);
          };

          返回 MicroApp 實(shí)例:

          • mount(): Promise;

          • unmount(): Promise;

          • update(customProps: object): Promise;

          • getStatus(): | "NOT_LOADED" | "LOADING_SOURCE_CODE" | "NOT_BOOTSTRAPPED" | "BOOTSTRAPPING" | "NOT_MOUNTED" | "MOUNTING" | "MOUNTED" | "UPDATING" | "UNMOUNTING" | "UNLOADING" | "SKIP_BECAUSE_BROKEN" | "LOAD_ERROR";

          • loadPromise: Promise;

          • bootstrapPromise: Promise;

          • mountPromise: Promise;

          • unmountPromise: Promise;

          詳情參考參考 qiankun 官方文檔

          3、subscribe(callback: (value: FMicroEvent) => void): Unsubscribe

          監(jiān)聽(tīng)主子應(yīng)用的事件和數(shù)據(jù),callback 函數(shù) ({type: string; data: any}) => { 處理程序 },目前通用返回加載完成事件 type: load,其他自定義事件由主子應(yīng)用自行定義。

          返回值:返回取消訂閱的句柄。

          其他接口在這里不做贅述了。

          Server 端的接口協(xié)議代理

          實(shí)現(xiàn)頁(yè)面低成本接入是微前端的重要愿景之一,也是吸引大家持續(xù)探索的核心原因。
          理想業(yè)務(wù)場(chǎng)景下,一個(gè)被微應(yīng)用改造之后的微應(yīng)用集成到其他業(yè)務(wù)系統(tǒng),應(yīng)該無(wú)需關(guān)心后臺(tái)接口,開(kāi)箱即用。但是后臺(tái)業(yè)務(wù)系統(tǒng)具有各自獨(dú)立的鑒權(quán)、賬戶、業(yè)務(wù)邏輯,相互之間差異性極大,完全無(wú)法做到開(kāi)箱即用。

          傳統(tǒng)的方式是主應(yīng)用去主動(dòng)接入微應(yīng)用的后臺(tái)系統(tǒng),缺點(diǎn)很明顯,主應(yīng)用要了解微應(yīng)用系統(tǒng)的后臺(tái)邏輯,且每接入一個(gè)系統(tǒng)就要重復(fù)上述的工作,成本高且低效。

          微前端脫胎于微應(yīng)用,我們也希望微應(yīng)用自己也實(shí)現(xiàn)微服務(wù)級(jí)別的邏輯自治,依托于騰訊云 API 托管,對(duì)外提供微應(yīng)用自身的服務(wù)。這樣的好處是主應(yīng)用只需要要對(duì)接騰訊云生態(tài),即可實(shí)現(xiàn)鑒權(quán)、賬號(hào)轉(zhuǎn)化、監(jiān)控等能力。主應(yīng)用和子應(yīng)用只需要做一次,無(wú)需重復(fù),缺點(diǎn)就是必須要依托于騰訊云生態(tài)。假如你的業(yè)務(wù)本身就是依托于微信、企業(yè)微信、騰訊云發(fā)展起來(lái)的,上述方式值得引入。

          微應(yīng)用改造

          一個(gè)業(yè)務(wù)系統(tǒng)并不是一開(kāi)始就有被集成的價(jià)值的,往往是在業(yè)務(wù)發(fā)展到一定程度,經(jīng)過(guò)市場(chǎng)驗(yàn)證其價(jià)值之后,大家才會(huì)明確這個(gè)業(yè)務(wù)系統(tǒng)具有微應(yīng)用改造的價(jià)值,這就導(dǎo)致一個(gè)窘迫的境地:你需要改造已經(jīng)成型的項(xiàng)目,使其成為可快速接入的微應(yīng)用。

          qiankun 的微應(yīng)用改造相對(duì)比較簡(jiǎn)單,一般在開(kāi)啟嚴(yán)格沙箱模式之后,微應(yīng)用和主應(yīng)用之間建立比較好的環(huán)境隔離,你并不需要太多的工作。

          首先樣式隔離,參考下面 FAQ 2、css 隔離 章節(jié)的介紹。其核心的麻煩在于 qiankun 啟動(dòng)嚴(yán)格沙箱木事之后,會(huì)導(dǎo)致 dialog、Modal 等組件無(wú)法找到 body 節(jié)點(diǎn),進(jìn)而無(wú)法掛在到 DOM 中。

          其次 JS 作用域隔離,這里主要是一些第三方庫(kù)會(huì)在 window 上掛在單例實(shí)例,導(dǎo)致主應(yīng)用和微應(yīng)用之間單例配置相互覆蓋,常見(jiàn)于日志上報(bào)、微信 SDK、QQ SDK 等第三方應(yīng)用。解決方案分為兩個(gè)方向:

          1. 假如主應(yīng)用存在則沿用主應(yīng)用的配置
            這種方式對(duì)主應(yīng)用比較有利。以日志上報(bào)的配置為例,微應(yīng)用的日志會(huì)上報(bào)到主應(yīng)用空間下,那么主應(yīng)用的日志監(jiān)控會(huì)很完整。缺點(diǎn)則是微應(yīng)用本身失去了這些監(jiān)控信息。

          2. 微應(yīng)用對(duì)自身使用的單例進(jìn)行隔離
            這種方式對(duì)微應(yīng)用比較有利。以 Axios 的配置為例,子應(yīng)用可以實(shí)現(xiàn)類(lèi)似中臺(tái)應(yīng)用的效果,可以探知到微應(yīng)用在不同的主應(yīng)用中的實(shí)際使用場(chǎng)景和數(shù)據(jù)統(tǒng)計(jì)。

          本文采用的是第二種方式,假如主應(yīng)用需要進(jìn)行數(shù)據(jù)共享或者配置共享,可以通過(guò)主應(yīng)用和微應(yīng)用之間的參數(shù)和數(shù)據(jù)傳遞的方式來(lái)實(shí)現(xiàn)共享,微應(yīng)用提供豐富的監(jiān)聽(tīng) hooks。

          FAQ

          1、如何解決 第三方 SDK JS 文件加載失敗問(wèn)題

          微信和企業(yè)微信的 SDK 是不可以自行構(gòu)建 Http 請(qǐng)求加載的,這是由于其安全策略導(dǎo)致的,且每次返回的內(nèi)容有安全限制的改動(dòng),無(wú)法復(fù)用。
          因此必須要在 html 的 header 中引入,但 qiankun 會(huì)對(duì) html header 所有 script link 資源構(gòu)造請(qǐng)求鏈接,進(jìn)而導(dǎo)致獲取第三方 SD K 的請(qǐng)求報(bào)錯(cuò),整個(gè) qiankun 客戶端加載微應(yīng)用進(jìn)程報(bào)錯(cuò),無(wú)法加載出對(duì)應(yīng)頁(yè)面。

          官網(wǎng)在常見(jiàn)問(wèn)題給了三個(gè)解決方案:

          1. 使用 getTemolate 過(guò)濾異常腳本;

          2. 使用自定義 fetch 阻斷 script 腳本;

          3. 終極方案 - 修改 html 的 content-type

          前兩種方案需要在乾坤渲染函數(shù)中增加一個(gè)對(duì)應(yīng)的參數(shù),這里面有個(gè)坑點(diǎn),則是 prefetchApps 不支持這些參數(shù),因此一旦啟用預(yù)加載函數(shù),則會(huì)導(dǎo)致渲染函數(shù)的傳入配置失效,因此需要關(guān)閉使用預(yù)加載函數(shù)。

          2、css 隔離

          微前端核心理念:解耦 / 技術(shù)棧無(wú)關(guān),簡(jiǎn)單來(lái)說(shuō)就是希望微應(yīng)用之間,基站應(yīng)用和微應(yīng)用之間的技術(shù)棧可以互相隔離,從而各種定制自己的技術(shù)體系來(lái)實(shí)現(xiàn)開(kāi)發(fā)效率和產(chǎn)品質(zhì)量的最優(yōu)化配置,這也是微前端的核心價(jià)值體現(xiàn)。

          然而理想是豐滿的,現(xiàn)實(shí)是骨感的。主流的沙箱模式是通過(guò)創(chuàng)建一個(gè)獨(dú)立的作用域隔離作用域鏈,同時(shí)克隆全局變量來(lái)實(shí)現(xiàn)的,但是這種隔離 + 克隆方案并不完美,在復(fù)雜運(yùn)行場(chǎng)景中,無(wú)論性能還是安全性都是難以保證的,特別是 CSS 的隔離。

          本文基于乾坤的微前端架構(gòu),在此基礎(chǔ)上做了一些查漏補(bǔ)缺的補(bǔ)充。

          首先是基礎(chǔ)頁(yè)面的 CSS,采用的是成熟的 CSS module 方案,簡(jiǎn)單來(lái)說(shuō)就是將 CSS 變成局部生效,每個(gè) class 生成一個(gè)獨(dú)一無(wú)二的名字。從最早的 Less、SASS,到后來(lái)的 PostCSS,再到最近的 CSS in JS,都是為了解決 CSS 全局生效帶來(lái)的副作用。

          css: {
          loaderOptions: {
          less: {
          javascriptEnabled: true,
          modifyVars: {
          '@ant-prefix': 'industryAnt', // 前綴
          'primary-color': '#0052d9',
          'link-color': '#0052d9',
          'btn-primary-color': '#ffffff',
          'btn-danger-bg': '#e34d59',
          'disabled-color': 'rgba(15, 24, 41, .3);',
          'btn-disable-bg': '#eaedf2',
          'text-color': '#0F1829',
          },
          module: true,
          },
          postcss: {
          plugins: [
          AddAntClass({ prefix: 'industryAnt' })
          ],
          }
          },
          }

          參考上面的配置,直接開(kāi)啟即可。

          在前端開(kāi)發(fā)過(guò)程中,我不可避免的會(huì)使用到各種前端的組件庫(kù),例如 antd、echarts,且都支持自定義主題配置,假如基站應(yīng)用和微應(yīng)用之間主題配置沖突了,就需要我們采用類(lèi)似 CSS module 的方案,將各自應(yīng)用的 CSS 應(yīng)用范圍控制在各自的組件控件內(nèi)。

          細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn),我上面的代碼就包含了 antd 的類(lèi)名定制的配置 - '@ant-prefix': 'xxxAnt' ,給所有 antd 組件增加類(lèi)名前綴。

          你以為到這里就完美解決了嗎?不,這才剛開(kāi)始。回到我們的業(yè)務(wù)場(chǎng)景中去,很多頁(yè)面的復(fù)用并不是一開(kāi)始就設(shè)計(jì)好的,往往是產(chǎn)品中后期發(fā)現(xiàn)業(yè)務(wù)體系之間存在高度的復(fù)用性,你需要對(duì)老的項(xiàng)目進(jìn)行改造使它支持微應(yīng)用架構(gòu)模式。經(jīng)驗(yàn)豐富的你發(fā)現(xiàn)已有項(xiàng)目中,存在很多全局 class 樣式,甚至全局地方類(lèi)庫(kù)的組件 class 樣式,如果手動(dòng)去調(diào)整,那絕對(duì) boom 。

          這里給大家提供一些工程化的方法。第三方類(lèi)庫(kù)的樣式類(lèi)名往往都是有通用類(lèi)名前綴,這是我們能夠解決這個(gè)問(wèn)題的基本前提條件,我們?cè)谠?CSS 構(gòu)建階段對(duì) css class 進(jìn)行替換和調(diào)整。有興趣的同學(xué)可以看下 less、 postcss 提供的插件機(jī)制,以下代碼僅供參考。

          const postcss = require('postcss');

          module.exports = postcss.plugin('postcss-add-css-prefix', function(opts = {}) {
          const {
          prefix = 'xxxAnt'
          } = opts

          // 接收兩個(gè)參數(shù),第一個(gè)是每個(gè)css文件的ast,第二個(gè)參數(shù)中可獲取轉(zhuǎn)換結(jié)果相關(guān)信息(包括當(dāng)前css文件相關(guān)信息)
          function plugin(css, result) {
          if (!prefix) return; // 沒(méi)傳入prefix,不執(zhí)行下面的邏輯
          css.walkRules(rule => { // 遍歷當(dāng)前ast所有rule節(jié)點(diǎn)
          const {
          selector
          } = rule;
          // 只有當(dāng)節(jié)點(diǎn)是ast根節(jié)點(diǎn)直屬子節(jié)點(diǎn)時(shí)才添加前綴
          // 簡(jiǎn)單做了容錯(cuò)處理,只要帶有根選擇器的都不添加前綴,本身帶有前綴了也不添加
          // 加了個(gè)flag,防止節(jié)點(diǎn)更新后重復(fù)執(zhí)行該邏輯進(jìn)入死循環(huán)
          if (rule.parent.type === 'root' && selector.indexOf('.ant-') >= 0 && selector.indexOf(`${prefix}-`) < 0 && !rule.flag) {
          rule.flag = true
          const clone = rule.clone();
          clone.selector = selector.split(' ').map(item => item.replace('.ant-', `.${prefix}-`)).join(' ');
          rule.replaceWith(clone)
          }
          })
          }

          return plugin
          })

          經(jīng)過(guò)以上調(diào)整,樣式終于隔離成功,微前端接入后展示完美,此時(shí)測(cè)試的反饋打破了你的幻想:confirm 的彈窗提示不顯示了。經(jīng)過(guò)排查發(fā)現(xiàn) ant-prefix + css 插件的方案,無(wú)法對(duì)動(dòng)態(tài)生成的組件樣式進(jìn)行調(diào)整,因此它的樣式會(huì)丟失。
          最終在 Ant Design of React 官方的 FAQ 中找到了線索,

          但是這個(gè)方案并不適用于本文使用 antdv 1.x 版本的微應(yīng)用項(xiàng)目,不支持這些 API。在 3.x 版本 FAQ 中給了一個(gè)推薦方案。

          這種方案是適用于 vue 3.x,對(duì)于 vue 2.x 的項(xiàng)目則需要使用 "@vue/composition-api" 來(lái)兼容 getCurrentInstance 等 API。經(jīng)過(guò)實(shí)踐發(fā)現(xiàn)本文的項(xiàng)目的 antv 的版本是 1.x 的,無(wú)法支持 appContext 參數(shù),最終探索了最終的解決方案。

          this.$confirm({
          prefixCls: `${ANT_PREFIX}-modal`,
          title: '正在上傳,確定要停止嗎?',
          cancelText: '取消',
          cancelButtonProps: {
          props: {
          prefixCls: `${ANT_PREFIX}-btn`,
          },
          },
          okText: '停止',
          centered: true,
          okButtonProps: {
          props: {
          type: 'danger',
          prefixCls: `${ANT_PREFIX}-btn`,
          },
          } asany,
          onOk: () => {
          },
          } asany)

          confirm 類(lèi)型定義中并不包含 prefixCls,因此需要使用 as any 忽略其類(lèi)型校驗(yàn),實(shí)際會(huì)透?jìng)鞯降讓?rc-dialog 組件中。


          ?? 謝謝支持

          以上便是本次分享的全部?jī)?nèi)容,希望對(duì)你有所幫助^_^

          喜歡的話別忘了 分享、點(diǎn)贊、收藏 三連哦~。

          歡迎關(guān)注公眾號(hào) 趣談前端 收貨大廠一手好文章~



          從零搭建全棧可視化大屏制作平臺(tái)V6.Dooring

          從零設(shè)計(jì)可視化大屏搭建引擎

          Dooring可視化搭建平臺(tái)數(shù)據(jù)源設(shè)計(jì)剖析

          可視化搭建的一些思考和實(shí)踐

          基于Koa + React + TS從零開(kāi)發(fā)全棧文檔編輯器(進(jìn)階實(shí)戰(zhàn)




          點(diǎn)個(gè)在看你最好看

          瀏覽 61
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  欧美AAA性爱 | 亚洲午夜无码久久久A | 欧美一级二级无人区精品 | 久青草中文在线观看 | 欧美日韩三区 |