<acronym id="indot"><dfn id="indot"></dfn></acronym>
<span id="indot"></span>

<bdo id="indot"><meter id="indot"></meter></bdo>
<label id="indot"><samp id="indot"></samp></label>
<label id="indot"><xmp id="indot">
  • <span id="indot"><table id="indot"></table></span>
    <center id="indot"><optgroup id="indot"></optgroup></center>
  • <bdo id="indot"><meter id="indot"></meter></bdo>
      當前位置:首頁 > javascript > 正文內容

      微前端使用: qiankun

      hxing6412年前 (2024-01-02)javascript3910

      一、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文件,添加跨域處理,避免跳轉時出現跨域問題

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


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

      // App.vue
      
      <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>

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

      // public-path.js
      if (window.__POWERED_BY_QIANKUN__) {
        // eslint-disable-next-line no-undef
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
      }
      // router/index.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 注冊和引入子應用

      // main.js
      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()

      2. 搭建子應用1

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

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

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

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

      // router/index.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: [
        ]
      })
      export default router

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

      // 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')

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

      // 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}`,
            },
        },
      })

      3. 搭建子應用2

      步驟與第2步類似,只是使用Vue2作為基礎框架


      三、功能演示

      2310605-20231219142600163-1643560325.gif



       四、常見問題

      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 文件。

      // actions.js
      import { initGlobalState } from "qiankun";
      const initialState = {
        token: 'no token'
      };
      const actions = initGlobalState(initialState);
      export default actions;

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

      // App.vue
      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);
          }
        }
      }

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

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

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

       // 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) })
          }
        }
      }

      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才可以跳轉

      掃描二維碼推送至手機訪問。

      版權聲明:本文由星星博客發布,如需轉載請注明出處。

      本文鏈接:http://www.7811333.com/?id=498

      分享給朋友:

      “微前端使用: qiankun” 的相關文章

      js fetch方法使用

      // get請求,第一個參數是請求地址,第二個可選,默認發送的是get請求 fetch('/api/user/list').then(res => {     res.json().then(data&n...

      js模塊化歷程

      這是一篇關于js模塊化歷程的長長的流水賬,記錄js模塊化思想的誕生與變遷,展望ES6模塊化標準的未來。經歷過這段歷史的人或許會感到滄桑,沒經歷過的人也應該知道這段歷史。 無模塊時代在ajax還未提出之前,js還只是一種“玩具語言”,由Brendan Eich花了不到十天時間發明,用來在網頁...

      JavaScript中的setTimeout和setInterval

      JavaScript是一門廣泛應用于網頁開發的腳本語言,它提供了許多強大的功能和特性。其中,setTimeout和setInterval是兩個常用的定時器函數,它們可以在指定的時間間隔內執行代碼。本文將深入探討這兩個函數的用法和特點,并通過代碼示例來加深理解。...

      JavaScript中的call、bind和apply方法深度解析

      JavaScript是一種動態的、強大的編程語言,它的靈活性源于其獨特的函數調用方式。在JavaScript中,我們常常會遇到三個非常重要的函數方法:call、bind和apply。這些方法都是用來改變函數運行時this的指向的。理解它們的工作原理和使用場景,對于我們編寫高質量的Java...

      7個Js async/await高級用法

      JavaScript的異步編程已經從回調(Callback)演進到Promise,再到如今廣泛使用的async/await語法。后者不僅讓異步代碼更加簡潔,而且更貼近同步代碼的邏輯與結構,大大增強了代碼的可讀性與可維護性。在掌握了基礎用法之后,下面將介紹一些高級用法,以便充分利用async/awai...

      JS數組常用方法

      JS數組常用方法

      JS數組常用方法:1.數組的復制和填充批量復制方法 copyWithin(),以及填充數組方法fill()。這兩個方法的函數簽名類似,都需要指定既有數組實例上的一個范圍,包含開始索引,不包含結束索引。使用這個方法不會改變數組的大小。 1.1.fill()方法使用fill()方法可以向一個已...

      發表評論

      訪客

      ◎歡迎參與討論,請在這里發表您的看法和觀點。
      主站蜘蛛池模板: 亚洲国产综合专区电影在线 | 国产精品亚洲综合网站| 中文字幕人成无码人妻综合社区 | 伊人久久婷婷五月综合97色| 亚洲国产精品成人综合色在线| 国产精品免费综合一区视频| 偷自拍视频区综合视频区| 婷婷综合激六月情网| 奇米综合四色77777久久| 久久88色综合色鬼| 亚洲欧美成人综合久久久| 亚洲一区综合在线播放| 九九综合VA免费看| 麻豆久久婷婷五月综合国产| 色婷婷综合缴情综免费观看| 亚洲乱码中文字幕小综合| heyzo专区无码综合| 一本一道久久综合久久| 久久婷婷五月综合97色| 国产成人无码综合亚洲日韩| 精品国产国产综合精品| 色噜噜狠狠狠色综合久| 亚洲综合在线一区二区三区| 亚洲国产精品成人综合久久久| 国产综合成人久久大片91| 国产福利电影一区二区三区久久久久成人精品综合 | 亚洲国产精品综合久久2007| 人人狠狠综合久久88成人| 亚洲国产精品综合久久网各| 亚洲人成在久久综合网站| 婷婷色中文字幕综合在线| 亚洲欧洲综合在线| AV狠狠色丁香婷婷综合久久| 亚洲综合久久精品无码色欲| 色婷婷久久综合中文久久一本`| 亚洲AV综合永久无码精品天堂 | 色狠台湾色综合网站| 色偷偷亚洲第一综合网| 国产精品亚洲综合一区| 久久精品亚洲综合| 亚洲国产精品综合久久2007|