qiankun 微前端實例化使用

来源:https://www.cnblogs.com/codeOnMar/archive/2023/12/19/17860009.html
-Advertisement-
Play Games

一、qiankun使用場景 1. 簡介:qiankun是在single-spa的基礎上實現的,可以保證各個項目獨立使用,也可以集成使用。各系統之間不受技術棧的限制,集成使用也能保證各樣式和全局變數的隔離。 模塊的插拔式使用,當公司項目集是一個大系統下包含多個子系統或者模塊時,可以採用這種方式動態部署 ...


一、qiankun使用場景

  1. 簡介:qiankun是在single-spa的基礎上實現的,可以保證各個項目獨立使用,也可以集成使用。各系統之間不受技術棧的限制,集成使用也能保證各樣式和全局變數的隔離。

      模塊的插拔式使用,當公司項目集是一個大系統下包含多個子系統或者模塊時,可以採用這種方式動態部署各個系統。

      亦或者是老項目技術升級和重構,可以通過qiankun按模塊進行改造,避免對整個系統產生較大的影響。

      功能和iframe類似,但是由於iframe數據通信難度較大,且有安全和SEO的問題,所以iframe使用體驗不佳。

  2. 原理邏輯:

    a. 需要在各個子應用的基礎上新增一個主應用,通過主應用監聽路由變化。

    b. 當有路由切換時就會觸發上述監聽函數從而去匹配在主應用中註冊的各個子應用路徑(activeRule)是否匹配。

    c. 匹配到子應用後就會載入子應用的資源到對應的容器當中去。

二、實現樣例

  本樣例使用的是Node 16的版本,主應用採用Vue3框架,兩個子應用分別使用Vue2和Vue3框架。qiankun版本是2.10.16。

  1. 搭建主應用,利用腳手架創建一個qiankun-main的主應用,同時安裝qiankun組件(qiankun只需要在主應用安裝,子應用不需要),其中代碼中標註重點的內容是配置qiankun的關鍵步驟

     1.1 打開vue.config.js文件,添加跨域處理,避免跳轉時出現跨域問題

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 8085,
    headers: {            // 重點1: 允許跨域訪問子應用頁面
        'Access-Control-Allow-Origin': '*',
    }
  }
})
vue.config.js

     1.2 主應用中設置子應用接收容器

<template>
  <div class="app">
    <p><router-link to="/">點擊跳轉到父頁面</router-link></p>
    <button @click="login">登陸</button>
    <p><router-link to="/vue3">跳轉到Vue3子應用</router-link></p>
    <p><router-link to="/vue2">跳轉到Vue2子應用</router-link></p>
    <router-view />
    <div id="vue3"></div>    <!-- 重點2:子應用容器 id -->
    <div id="vue2"></div> <!-- 重點2:子應用容器 id -->
  </div>
</template>

<script>
import actions from '@/shared/actions';

export default {
  name: 'App',
  components: {
  },
  mounted() {
    actions.onGlobalStateChange((state, prevState) => {
      // state: 變更後的狀態; prevState: 變更前的狀態
      console.log('主應用觀察者:token值改為', prevState.token);
      console.log("主應用觀察者:登錄狀態發生改變,改變後的 token 的值為 ", state.token);
    });
  },
  methods: {
    login() {
      console.log('進入登陸事件');
      setTimeout(() => {
        const token = 'token_' + Math.floor(Math.random() * 100000);
        //登陸後隨機生成token並設置
        actions.setGlobalState({ token });
        this.$router.push("/vue3");
      }, 300);
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
App.vue

    1.3 在src根目錄下新增public-path文件;同時改造路由,設置返回的base地址

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
public-path.js
import {
  createRouter,
  createWebHashHistory
} from 'vue-router'
import '../public-path' // 重點3: 引入public-path文件
const router = createRouter({
  base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/', // 重點4:qiankun進入子應用時,返回true
  history: createWebHashHistory(), // 重點5
  routes: [{
          path: '/',
          redirect: '/child'
      },
      {
          path: '/child',
          component: () => import('@/components/child')
      }
  ]
})
export default router
router/index.js

    1.4 註冊和引入子應用

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun'

createApp(App).use(router).mount('#app')

registerMicroApps([
  {
    name: "vue3 app",
    entry: "//localhost:8086", // 重點8:對應重點6
    container: '#vue3',         // 重點9:對應重點2
    activeRule: '/#/vue3',        // 重點10:對應重點4
    props: {
        appContent: '我是主應用傳給vue的值'
    }
  },
  {
    name: "vue2 app",
    entry: "//localhost:8087", // 重點8:對應重點6
    container: '#vue2',         // 重點9:對應重點2
    activeRule: '/#/vue2',        // 重點10:對應重點4
    props: {
        appContent: '我是主應用傳給Vue2的值'
    }
  }
])
setDefaultMountApp("/")  // 重點11:啟動預設的子模塊
// 啟動
start()
main.js

  2. 搭建子應用1, 同樣利用腳手架創建一個qiankun-vue3-child,項目使用Vue3作為基礎框架

    2.1 同樣在src目錄下創建public-path.js文件

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
public-path.js

    2.2 改造router/index.js文件, 確認在qiankun模式下的路由基礎路徑

import {
  createRouter,
  createWebHashHistory
} from 'vue-router'
import '../public-path' // 重點3: 引入public-path文件
const router = createRouter({
  base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/', // 重點4:qiankun進入子應用時,返回true
  history: createWebHashHistory(), // 重點5
  routes: [
  ]
})
export default router
router/index.js

    2.3 修改入口函數main.js,導入鉤子函數

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import actions from './micros/actions'

let instance = null

function render(props = {}) {
  // qiankun模式下實現父子應用之間通信
  if (props) {
    actions.setActions(props);
  }

  const { container } = props
  // 為了避免根id#app與其他DOM衝突,需要限制查找範圍
  instance = createApp(App).use(router).mount(container ? container.querySelector('#child-app') : '#child-app')
}

if (!window.__POWERED_BY_QIANKUN__) {
    render()
}

//--------- 生命周期函數------------//
export async function bootstrap() {
  console.log('[vue] vue app bootstraped')
}
export async function mount(props) {
  console.log('[vue] props from main framework', props)
  render(props)
}
export async function unmount() {
  if (instance) {
    console.log(instance, instance.unmount);
    // instance.unmount();
    instance = null
  }
}

// createApp(App).use(router).mount('#child-app')
main.js

    2.4 修改打包配置文件vue.config.js,設置服務埠以及打包模式

const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 8086,            // 重點6
    headers: {            // 重點7:同重點1,允許子應用跨域
        'Access-Control-Allow-Origin': '*',
    },
  },
  // 自定義webpack配置
  configureWebpack: {
      output: {
          library: `${name}-[name]`,
          libraryTarget: 'umd',        // 把子應用打包成 umd 庫格式
          // jsonpFunction: `webpackJsonp_${name}`,
      },
  },
})
vue.config.js

  3. 搭建子應用2,步驟與第2步類似,只是使用Vue2作為基礎框架

三、功能演示

 四、常見問題

  1. 子應用部署在同一個伺服器同一個埠的不同路徑下如何配置?

    基本和部署在不同伺服器的類似,只是將註冊子應用的entry的伺服器埠號換成某個路徑,同時將打包的publicPath改為該路徑

// 主應用入口文件中註冊子應用
registerMicroApps([
  {
    name: "vue3_app",
    entry: "/entry_vue3", // 對應之前的 //localhost:8086
    container: '#vue3',    
    activeRule: '/#/vue3',     
    props: {
        appContent: '我是主應用傳給vue的值'
    }
  }
])

// 子應用的 router/indexedDB.js
const router = createRouter({
  base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/entry_vue3', // 設置路由路徑
  history: createWebHashHistory(),
  routes: [
  ]
})

// 打包文件vue.config.js中添加預設路徑
module.exports = defineConfig({
  publicPath: devFlag ? '/' : '/entry_vue3',
  transpileDependencies: true,
  devServer: {
    port: 8087,            // 重點6
    headers: {            // 重點7:同重點1,允許子應用跨域
        'Access-Control-Allow-Origin': '*',
    },
  },
  // 自定義webpack配置  重點12
  configureWebpack: {
      output: {
          library: `${name}-[name]`,
          libraryTarget: 'umd',        // 把子應用打包成 umd 庫格式
          // jsonpFunction: `webpackJsonp_${name}`,
      },
  },
})
同伺服器同埠部署配置

  2. 主子應用之間通信?

    2.1 使用qiankun框架提供的 initGlobalState 實現的,主要有下麵三個函數:

      onGlobalStateChange(callback, Immediately)在當前應用監聽全局狀態變化;

      setGlobalState(state)按照一級屬性進行狀態設置,微應用只能修改一級屬性;

      offGlobalStateChange()移除當前的狀態監聽,微應用在unmount時預設調用;

    2.2 使用方式,效果可見上面的案列圖中對token的列印信息

      a. 主應用的src目錄下新增shared/actions.js文件。

import { initGlobalState } from "qiankun";

const initialState = {
  token: 'no token'
};
const actions = initGlobalState(initialState);

export default actions;
actions.js

      b. 比如在主應用的App.vue中使用並且實現登陸後生成token以及跳轉到vue3子應用

import actions from '@/shared/actions';

export default {
  name: 'App',
  components: {
  },
  mounted() {
    actions.onGlobalStateChange((state, prevState) => {
      // state: 變更後的狀態; prevState: 變更前的狀態
      console.log('主應用觀察者:token值改為', prevState.token);
      console.log("主應用觀察者:登錄狀態發生改變,改變後的 token 的值為 ", state.token);
    });
  },
  methods: {
    login() {
      console.log('進入登陸事件');
      setTimeout(() => {
        const token = 'token_' + Math.floor(Math.random() * 100000);
        //登陸後隨機生成token並設置
        actions.setGlobalState({ token });
        this.$router.push("/vue3");
      }, 300);
    }
  }
}
App.vue

      c. 子應用中使用時首先在根目錄下創建一個micros/actions.js文件

function emptyAction() {
  // 確保單獨部署時不會報錯
  console.warn('當前無可執行的Action');
}

class Actions {
  // 預設設置空Action
  actions = {
    onGlobalStateChange: emptyAction,
    setGlobalState: emptyAction
  }

  // 設置Actions
  setActions(actions) {
    this.actions = actions;
  }

  // 映射
  onGlobalStateChange(...args) {
    return this.actions.onGlobalStateChange(...args);
  }

  // 映射
  setGlobalState(...args) {
    return this.actions.setGlobalState(...args);
  }
}

const actions = new Actions();
export default actions;
actions.js

      d. 子應用的APP.vue頁面中監聽主應用中數據的變化以及子應用主動修改數據觀察主應用是否能接收到

import actions from '@/micros/actions.js';

export default {
  name: 'App',
  components: {
  },
  mounted() {
    actions.onGlobalStateChange(state => {
      console.log('子應用Vue的觀察函數:', state);
    }, true)
  },
  methods: {
    changeToken() {
      actions.setGlobalState({ token: 'Vue3_' + Math.floor(Math.random() * 100000) })
    }
  }
}
App.vue

  3. 各個應用之間如何提取一些公共的資源或者模塊?

    可以將公共模塊提取成一個公共組件發佈到npm,然後由各個應用按需安裝。

  4. 各個系統如何做到只登陸一次?

    可以參考單點登陸實現,簡單邏輯就是比如:

    a. 有一個地址sso.com做為控制中心,然後a.com、b.com為子模塊。

    b. 當訪問a.com時無許可權時路由會攜帶參數“a.com”自動跳轉到登陸頁面,輸入用戶名密碼信息後,經過sso.com驗證通過生成ticket並返回給頁面同時跳轉到a.com並下發ticket。

    c. a.com請求獲取到ticket後訪問sso.com的伺服器進行驗證是否有效,有效則允許登陸,這樣就完成了一次登陸。

    d. 如果在已登錄的狀態下跳轉到b.com,則省略第二步的登陸驗證直接將ticket攜帶到b.com,然後再訪問sso.com進行有消息驗證。

 

註意:主應用註冊的activeRule為/vue3時跳轉到子應用不生效可能是因為瀏覽器路由跳轉時自動加上/#/,所以在activeRule也需要修改為/#/vue3才可以跳轉


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 簡介 為什麼需要分散式配置中心 分散式配置中心是為瞭解決在分散式系統中進行配置管理的需求而引入的。在傳統的單體應用中,通常使用配置文件集中管理系統的配置信息。然而,在分散式系統中,由於系統規模變大、節點眾多,並且可能部署在不同的伺服器上,傳統的配置文件方式會面臨一些挑戰。 首先,配置文件的修改 ...
  • 概述:學習.NET中使用SignalR實現實時通信功能。從安裝庫、創建Hub,到客戶端基礎功能,一步步構建實時聊天室。深入講解分組功能,使您能夠定向廣播消息。簡潔實用,助您輕鬆掌握實現創新Web應用的技能。 SignalR是一個強大的實時通信庫,為.NET應用程式提供輕鬆的實時功能。它支持雙向通信, ...
  • 1.前言 又到了年底,最近在做年報數據拉取的需求,在這期間有一些數據的計算需要用到視窗函數,就去瞭解了一下常用的視窗函數的用法。 1.1.什麼是視窗函數 視窗函數是 SQL 中的一種特殊函數,它允許你在查詢結果中的某個視窗或視窗範圍上執行計算,而不是單獨針對整個結果集進行計算。 視窗函數通常與 OV ...
  • 綠色安裝實施步驟地址: https://blog.csdn.net/qq_39135287/article/details/82117234 簡略步驟: 1.到官網下載綠色安裝包(https://dev.mysql.com/downloads/mysql/) 2.解壓mysql包到指定的位置並新建d ...
  • 12月16日周六下午,由NineData、PostgreSQL中文社區、PolarDB開源社區共同舉辦的《國產資料庫共話未來趨勢》技術沙龍,在NineData的報告廳成功舉辦。本次沙龍匯聚阿裡雲、玖章算術、百度雲、飛輪科技、YMatrix、格睿科技、羲和Halo等眾多資料庫廠商的技術大咖,以及北京大... ...
  • 如今,大規模、高時效、智能化數據處理已是“剛需”,企業需要更強大的數據平臺,來應對數據查詢、數據處理、數據挖掘、數據展示以及多種計算模型並行的挑戰,湖倉一體方案應運而生。 《實時湖倉實踐五講》是袋鼠雲打造的系列直播活動,將圍繞實時湖倉的建設趨勢和通用問題,邀請奮戰於企業數字化一線的核心產品&技術專家 ...
  • 最近開始體驗FastGPT知識庫問答系統,參考官方文檔,在自己的阿裡雲伺服器使用Docker Compose快速完成了部署。 環境說明:阿裡雲ECS,2核8G,X86架構,CentOS 7.9操作系統。 Docker與Docker-Compose安裝 1.登錄伺服器,執行相關命令完成安裝。 # 安裝 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 你知道奇怪的移動視口錯誤(也稱為100vh bug)嗎?或者如何以正確的方式創建全屏塊? 一、100vh bug 什麼是移動視口錯誤? 你是否曾經在網頁上創建過全屏元素?只需添加一行 CSS 並不難: .my-page { height: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...