微前端框架(qiankun)配置

微前端框架(qiankun)使用記錄

參考:html

疑惑

  1. 主框架和子框架是否都要安裝乾坤框架
答:不是,只須要安裝在主框架便可,子框架不須要引入

使用方法

主應用

  1. 路由建議使用history模式
  2. 安裝 qiankun前端

    yarn add qiankun 或者 npm i qiankun -Svue

  3. 配置微應用webpack

    // /src/micro/apps.js
    const apps = [
        {
            name: 'planResource',
            entry: '//localhost:8083',
            container: '#iframe',
            activeRule: '/plan'
        },
        {
            name: 'configration',
            entry: '//localhost:8081',
            container: '#iframe',
            activeRule: '/configure'
        }
    ];
    
    export default apps;
  1. 在主應用中註冊微應用web

    // src/micro/index.js
    
    import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';
    
    import apps from './apps';
    
    // registerMicroApps 第二個參數能夠不要,若是要作點啥,就寫到對應的地方
    registerMicroApps(apps,
    {
      beforeLoad: [
        app => {
          console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
        },
      ],
      beforeMount: [
        app => {
          console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
        },
      ],
      afterUnmount: [
        app => {
          console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
        },
      ],
    },);
    
    // 出錯時顯示的內容
    addGlobalUncaughtErrorHandler((event) => {
      // console.log(event);
      const { message } = event;
      if (message && message.includes('died in status LOADING_SOURCE_CODE')) {
        console.log('微應用加載失敗,請檢查應用是否可運行');
      }
    });
    
    export default start;
  2. 使用 startvue-router

    // /src/main.js
    import microApp from './micro';
    
    new Vue({
      router,
      store,
      render: (h) => h(App)
    }).$mount('#app');
    
    microApp();

注意:vuex

<font color=orange>不必定要放在入口文件內,看你的項目而定</font>npm

  1. 若是你的微應用容器在index.html裏的話,是沒有多大問題,
  2. 可是不少時候是嵌入在Layout框架裏的,正常進入是沒有問題,若是是在微應用路徑下刷新頁面,會有以下圖片的報錯json

    <font color=red>Uncaught Error: application 'cloud' died in status LOADING_SOURCE_CODE: [qiankun] Target container with #cloud not existed while cloud loading!</font>bootstrap

這種狀況下建議在改組件的mounted事件鉤子下start, main.js下引入,不執行便可

eg:

//main.js
...
import microApp from './micro' // 加載乾坤app
microApp // 註冊應用


// 包含子應用容器的VUE組件
<template>
  <div class="vab-app-main">
    <section>
      <transition mode="out-in" name="fade-transform">
        <vab-keep-alive v-if="routerView" />
      </transition>
      <div id="cloud"></div>
      <div id="vue3"></div>
    </section>
    <vab-footer />
  </div>
</template>

<script>
  ...
  import { start } from 'qiankun'
  export default {
    name: 'VabAppMain',
    data() {
      return {
        ...
      }
    },
    ...
    mounted() {
      start({
        prefetch: false, // 取消預加載
      }) // 開啓
    },
    ...
  }
</script>

子應用

  1. 修改webpack配置

    // webpack.config.js || vue.config.js
    const port = 8088;
    const packageName = require("./package.json").name;
    module.exports = {
      lintOnSave: false, // 關閉eslint檢測
    
      devServer: {
        port, // 這裏的端口是必須和父應用配置的子應用端口一致
        disableHostCheck: false, // 關閉主機檢查,保證子應用能夠被主應用fetch到
        headers: {
          //由於qiankun內部請求都是fetch來請求資源,因此子應用必須容許跨域
          "Access-Control-Allow-Origin": "*",
        },
      },
      configureWebpack: {
        output: {
          //資源打包路徑
          library: `${packageName}`,
          libraryTarget: "umd",
          jsonpFunction: `webpackJsonp_${packageName}`,
          // publicPath: `//localhost:${port}`,
        },
      },
      // outputDir: 'dist',
      // assetsDir: 'static',
      // filenameHashing: true,
    };
  2. 增長 public-path配置文件並在入口文件引入

    //  /src/micro/public-path.js
    if (window.__POWERED_BY_QIANKUN__) {
        // eslint-disable-next-line no-undef
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    
    
    //  /src/main.js
    import '@/micro/public-path'
  1. 路由實例化配置

    // src/router/index.js
    
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    const routes = [
        {
            path: '/',
            name: 'Home',
            component: () => import('../views/Home.vue')
        },
        {
            path: '/about',
            name: 'About',
            component: () => import('../views/About.vue')
        }
    ]
    const router = new VueRouter({
        mode: 'history',
        // @ts-ignore
        base: window.__POWERED_BY_QIANKUN__ ? '/cloud' : '/', // 獨立運行是用"/"做爲根路徑
        // base: '/cloud',
        routes
    })
    export default router
  2. Vue實例化包裝和生命週期鉤子導出

    // /src/main.js
    
    import "@/micro/public-path";
    import Vue from "vue";
    import App from "./App.vue";
    import actions from "./micro/actions.js";
    import router from "./router";
    import store from "./sotre";
    let instance = null;
    
    function render(props = {}) {
      // 通過實踐這個判斷要有,否則在獨立運行時報錯
      if (window.__POWERED_BY_QIANKUN__) {
        if (props) {
          actions.setActions(props);
          actions.onGlobalStateChange((state) => {
            console.log("app1", state);
          }, true);
        }
      }
      const { container } = props;
      // 這裏是掛載到本身的html中  基座會拿到這個掛載後的html 將其插入進去
      const renderContainer = container ? container.querySelector("#app") : "#app";
      instance = new Vue({
        router,
        store,
        render: (h) => h(App),
      }).$mount(renderContainer);
    }
    
    // @ts-ignore
    if (!window.__POWERED_BY_QIANKUN__) {
      // 默認獨立運行
      render();
    }
    
    // 父應用加載子應用,子應用必須暴露三個接口:bootstrap、mount、unmount
    export async function bootstrap() {
      console.log("cloud app bootstraped");
    }
    
    export async function mount(props) {
      console.log("mount");
      render(props);
    }
    
    export async function unmount() {
      console.log("unmount");
      instance.$destroy();
    }

傳值 <font color=red>Actions</font>

props 傳值不夠靈活,不推薦使用

<font color=red>Actions</font>推薦使用方法,能夠在狀態改變時觸發

主應用

  1. 初始化全局跨應用通訊方法
// /src/micro/actions.js

import { initGlobalState } from 'qiankun';
const initState = {};
const actions = initGlobalState(initState);
export default actions;
  1. 在須要設置跨應用全局狀態的組件內:
// /src/app.vue
<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
  import actions from '@/micro/actions'
  export default {
    name: 'App',
    computed: {
      visitedRoutes() {
        return this.$store.state.tabs.visitedRoutes //須要監聽的數據
      },
    },
    watch: {
      visitedRoutes(newVal, oldVal) {
        this.setGlobalState()
      },
    },
    created() {
      // this.setGlobalState()
    },
    methods: {
      setGlobalState() {
        let data = {
          time: this.$store.state.user.time,
          token: this.$store.state.user.token,
          ability: this.$store.state.acl,
          routes: this.$store.state.routes,
          tabs: this.$store.state.tabs.visitedRoutes,
        }
        actions.setGlobalState(data)
      },
    },
  }
</script>

注意:

<font color=orange>能夠監聽vuex數據,若是變化,執行setGlobalState</font>

子應用

  1. 全局actions定義
//  /src/micro/actions.js

function emptyAction() {
    // 警告:提示當前使用的是空 Action
    console.warn('Current execute action is empty!');
  }
  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;
  1. 在應用渲染前獲取從主應用上的通訊方法並注入到actions裏
// /src/main.js

import "@/micro/public-path";
import Vue from "vue";
import App from "./App.vue";
import actions from "./micro/actions.js";
import router from "./router";
import store from "./sotre";
let instance = null;

function render(props = {}) {
  // @ts-ignore
  if (window.__POWERED_BY_QIANKUN__) {
    if (props) {
      actions.setActions(props);
      actions.onGlobalStateChange((state) => {
        console.log("app1", state);
        const { token, ability, tabs } = state;
        store.state.ability = ability
        store.state.token = token
        store.state.tabs = tabs
      }, true);
    }
  }
  const { container } = props;
  // 這裏是掛載到本身的html中  基座會拿到這個掛載後的html 將其插入進去
  const renderContainer = container ? container.querySelector("#app") : "#app";
  instance = new Vue({
    router,
    store,
    render: (h) => h(App),
  }).$mount(renderContainer);
}

// @ts-ignore
if (!window.__POWERED_BY_QIANKUN__) {
  // 默認獨立運行
  render();
}

// 父應用加載子應用,子應用必須暴露三個接口:bootstrap、mount、unmount
export async function bootstrap() {
  console.log("cloud app bootstraped");
}

export async function mount(props) {
  console.log("mount");
  render(props);
}

export async function unmount() {
  console.log("unmount");
  instance.$destroy();
}

開發DevTools

若是基座和子應用使用的都是VUE,要使用devtools是比較老火的事情:

  1. 若是使用的devtools 5.xx 的版本,那麼只能查看主框架的數據
  2. 若是使用devtools 6.xx(目前只有bata版本),且基座和子應用使用不一樣的VUE版本,那麼能夠查看對應應用的數據,<font color=orange>但是開發開發工具會不停的報錯,蛋痛</font>

建議:基座框架和子框架使用不一樣的框架語言開發(這樣是否要好一些,或者有解決的辦法)

<font color=red>主應用在何時傳值</font>

很重要:否則首次是拿不到值的哦

參考傳值Actions 主應用下的設置 監聽vuex

相關文章
相關標籤/搜索