qiankun搭建項目

什么是微前端

微前端是指存在于瀏覽器中的微服務(wù),其借鑒了微服務(wù)的架構(gòu)理念旦装,將微服務(wù)的概念擴展到了前端奇唤。

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

基于qiankun的微前端實戰(zhàn)

這里準(zhǔn)備三個項目犀忱,基座使用react項目募谎,兩個子應(yīng)用一個使用react18,一個使用vue3阴汇。

├── base-mrc     // 基座
├── mrc-react       // react子應(yīng)用数冬,create-react-app創(chuàng)建的react應(yīng)用,使用webpack打包
├── mrc-vue  // vue子應(yīng)用搀庶,vite創(chuàng)建的子應(yīng)用

基座配置拐纱,這里用react項目配置基座

主要負責(zé)集成所有的子應(yīng)用,提供一個入口能夠訪問你所需要的子應(yīng)用的展示哥倔,盡量不寫復(fù)雜的業(yè)務(wù)邏輯 - 子應(yīng)用:根據(jù)不同業(yè)務(wù)劃分的模塊秸架,每個子應(yīng)用都打包成umd模塊的形式供基座(主應(yīng)用)來加載。

  1. 安裝qiankun
npm i qiankun // 或者 yarn add qiankun

2咆蒿、入口文件配置(index.js)

import { start, registerMicroApps } from 'qiankun';
const apps = [
  {
    name: "mrcReact", // 子應(yīng)用的名稱
    entry: 'http://localhost:8081/mrcReact/', // 默認會加載這個路徑下的html东抹,解析里面的js
    activeRule: "/mrcReact", // 匹配的路由
    container: "#container" // 加載的容器
  },
  {
    name: "mrcVue", // 子應(yīng)用的名稱
    entry: 'http://localhost:8082/mrcVue/', // 默認會加載這個路徑下的html,解析里面的js
    activeRule: "/mrcVue", // 匹配的路由
    container: "#container" // 加載的容器
  },
]

// 2. 注冊子應(yīng)用
setTimeout(() => {
  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)],
  })
  
  start();
})

3沃测、在頁面上添加id為container的占位符缭黔,用來加載子應(yīng)用

    <div className="root">
      <div className="menu">
        <div className="menu-item" onClick={()=>nav('/mrcReact')}>首頁</div>
        <div className="menu-item" onClick={()=>nav('/mrcVue')}>新聞</div>
      </div>
      <div className="cont"><Outlet /><div id='container'></div></div>
    </div>

react子應(yīng)用配置

使用create-react-app腳手架創(chuàng)建,webpack進行配置芽突,為了不eject所有的webpack配置试浙,我們選擇用react-app-rewired工具來改造webpack配置。

1入口文件配置(index.js)

// 防止資源加載錯位
import './public-path.js';

//qiankun環(huán)境下應(yīng)用掛在到基座的root元素下
let root;
function render(props) {
  const { container } = props
  const dom = container ? container.querySelector('#root') : document.getElementById('root')
  root = ReactDOM.createRoot(dom)
  root.render(
    <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/mrcReact' : '/'}>
      <App />
    </BrowserRouter>
  )
}

// 判斷是否在qiankun環(huán)境下寞蚌,非qiankun環(huán)境下獨立運行
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

export async function bootstrap() {
  console.log('react app bootstraped');
}

// qiankun環(huán)境下田巴,應(yīng)用每次進入都會調(diào)用 mount 方法,通常我們在這里觸發(fā)應(yīng)用的渲染方法
export async function mount(props) {
    render(props);
}

// 應(yīng)用每次 切出/卸載 會調(diào)用的方法挟秤,通常在這里我們會卸載微應(yīng)用的應(yīng)用實例
export async function unmount(props) {
  root.unmount();
}

2壹哺、資源加載(public-path.js)

if (window.__POWERED_BY_QIANKUN__) {
    // 動態(tài)設(shè)置 webpack publicPath,防止資源加載出錯
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

3艘刚、配置webpack改造打包方式(config-overrides.js)
子應(yīng)用打包方式改成umd方式并在請求頭添加跨域設(shè)置

const { name } = require("./package");

process.env.PORT = 8081;
module.exports = {
  webpack: (config) => {
    config.output.library = `${name}-[name]`;
    config.output.libraryTarget = 'umd';
    // If you are using webpack 5, please replace jsonpFunction with chunkLoadingGlobal
    config.output.chunkLoadingGlobal = `webpackJsonp_${name}`;
    config.output.globalObject = 'window';
    return config;
  },
  devServer: (_) => {
    const config = _;

    config.headers = {
      'Access-Control-Allow-Origin': '*',
    };
    config.historyApiFallback = true;
    config.hot = false;
    config.watchContentBase = false;
    config.liveReload = false;

    return config;
  },
};

vue子應(yīng)用配置

1入口文件配置(main.js)

import './public-path'

let app;
if (!window.__POWERED_BY_QIANKUN__) {
    createApp(App).mount('#app');
}
  
export async function bootstrap() {
    console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
    app = createApp(App);
    console.log("props.container.querySelector('#app'):", props.container.querySelector('#app'));
    app.mount(props.container.querySelector('#app'));
}
export async function unmount() {
    app?.unmount();
}

2管宵、資源加載(public-path.js)

if (window.__POWERED_BY_QIANKUN__) {
    // 動態(tài)設(shè)置 webpack publicPath,防止資源加載出錯
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

3攀甚、配置webpack改造打包方式(vue.config.js)

const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
  transpileDependencies: true,
  publicPath: '/mrcVue',
  devServer: {
    port: 8082,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd', // bundle the micro app into umd library format
      chunkLoadingGlobal: `webpackJsonp_${name}`, // // If you are using webpack 5, please replace jsonpFunction with chunkLoadingGlobal
    },
  },
})

問題匯總

1箩朴、在基座中刷新某個子應(yīng)用時而能出來時而加載不出來(子應(yīng)用加載快于基座,導(dǎo)致找不到根節(jié)點)秋度,解決方案:延遲加載子應(yīng)用

setTimeout(() => {
  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)],
  })
  start();
})

2炸庞、qiankun實現(xiàn)了各個子應(yīng)用之間的樣式隔離,但是基座和子應(yīng)用之間的樣式隔離沒有實現(xiàn)荚斯,所以基座和子應(yīng)用之前的樣式還會有沖突和覆蓋的情況埠居,解決方法:1查牌、每個應(yīng)用的樣式使用固定的格式 2、通過css-module的方式給每個應(yīng)用自動加上前綴

//  子應(yīng)用配置css module
css: {
    loaderOptions: {
      css: {
        modules: {
          auto: () => true   /* 樣式會被編譯獨一無二的字段滥壕,需要通過引用變量的形式加載樣式 */
        }
      }
    }
  }

// 加載樣式
import styles from "./Header.module.css";
export default function Header() {
  return <h2 className={styles.title}>Header 組件</h2>;
}

3纸颜、父子應(yīng)用的通信
基座引用qiankun框架的initGlobalState屬性注冊全局狀態(tài),子應(yīng)用通過生命周期props可以發(fā)送和接收數(shù)據(jù)绎橘。

// 基座通過onGlobalStateChange監(jiān)聽數(shù)據(jù)
let state = {msg: ''}
const actions = initGlobalState(state);
// 主項目項目監(jiān)聽和修改
actions.onGlobalStateChange((state, prev) => {
  console.log("父應(yīng)用接受到數(shù)據(jù):", state, prev);
});

// 子應(yīng)用通過setGlobalState發(fā)送數(shù)據(jù)
export async function mount(props) {
    setInterval(() => {
        props.setGlobalState({msg: 666});
    }, 5000);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胁孙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子金踪,更是在濱河造成了極大的恐慌浊洞,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胡岔,死亡現(xiàn)場離奇詭異法希,居然都是意外死亡,警方通過查閱死者的電腦和手機靶瘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門苫亦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怨咪,你說我怎么就攤上這事屋剑。” “怎么了诗眨?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵唉匾,是天一觀的道長。 經(jīng)常有香客問我匠楚,道長巍膘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任芋簿,我火速辦了婚禮峡懈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘与斤。我一直安慰自己肪康,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布撩穿。 她就那樣靜靜地躺著磷支,像睡著了一般。 火紅的嫁衣襯著肌膚如雪食寡。 梳的紋絲不亂的頭發(fā)上齐唆,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天制恍,我揣著相機與錄音,去河邊找鬼右蹦。 笑死萨惑,一個胖子當(dāng)著我的面吹牛增拥,可吹牛的內(nèi)容都是我干的重付。 我是一名探鬼主播填硕,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼柜裸,長吁一口氣:“原來是場噩夢啊……” “哼擂错!你這毒婦竟也來了味滞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钮呀,失蹤者是張志新(化名)和其女友劉穎剑鞍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爽醋,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蚁署,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚂四。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光戈。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遂赠,靈堂內(nèi)的尸體忽然破棺而出久妆,到底是詐尸還是另有隱情,我是刑警寧澤跷睦,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布筷弦,位于F島的核電站,受9級特大地震影響抑诸,放射性物質(zhì)發(fā)生泄漏烂琴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一哼鬓、第九天 我趴在偏房一處隱蔽的房頂上張望监右。 院中可真熱鬧,春花似錦异希、人聲如沸健盒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扣癣。三九已至,卻和暖如春憨降,著一層夾襖步出監(jiān)牢的瞬間父虑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工授药, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留士嚎,地道東北人呜魄。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像莱衩,于是被迫代替她去往敵國和親爵嗅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容