乐趣区

关于vue.js:微前端框架qiankun配置

微前端框架 (qiankun) 应用记录

参考:

  • https://qiankun.umijs.org/zh/…
  • https://blog.csdn.net/hjb2722…
  • 其余

纳闷

  1. 主框架和子框架是否都要装置乾坤框架

答:不是,只须要装置在主框架即可,子框架不须要引入

应用办法

主利用

  1. 路由倡议应用 history 模式
  2. 装置 qiankun

    yarn add qiankun 或者 npm i qiankun -S

  3. 配置微利用

    // /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. 在主利用中注册微利用

    // 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. 应用 start

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

留神:

<font color=orange> 不肯定要放在入口文件内,看你的我的项目而定 </font>

  1. 如果你的微利用容器在 index.html 里的话,是没有多大问题,
  2. 然而很多时候是嵌入在 Layout 框架里的,失常进入是没有问题,如果是在微利用门路下刷新页面,会有如下图片的报错

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

这种状况下倡议在改选件的 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

退出移动版