vue3+ts+qiankun 初体验

166 阅读3分钟

qiankun基本介绍

qiankun 是一个基于 single-spa微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。qiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台,在经过一批线上应用的充分检验及打磨后,我们将其微前端内核抽取出来并开源,希望能同时帮助社区有类似需求的系统更方便的构建自己的微前端系统,同时也希望通过社区的帮助将 qiankun 打磨的更加成熟完善。目前 qiankun 已在蚂蚁内部服务了超过 2000+ 线上应用,在易用性及完备性上,绝对是值得信赖的。

demo搭建

主应用main-pro搭建

main.ts注册子应用

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import actions from "./globalState";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import { registerMicroApps, start } from "qiankun";
// 创建vue实例
const app = createApp(App);
const msg = "小飞";
createApp(App).use(ElementPlus).use(router).mount("#main-app");
registerMicroApps(
  [
    {
      name: "child-pro",
      entry: process.env.VUE_APP_SUB_VUE1,
      container: "#container",
      activeRule: "/micrApp/child-pro", //路由
      props: {
        msg,
        actions,
      },
    },
  ],
  {}
);
// 启动 qiankun
start();

app.vue 新建子应用挂载节点container

<template>
  <div class="box">
    <div id="container"></div>
    <router-view />
  </div>
</template>

globalState.ts 全局状态管理文件,用来进行主应用与子应用以及子应用之间数据传递

import { initGlobalState, MicroAppStateActions } from "qiankun";
import { reactive } from "vue";
​
type keys = keyof typeof initialState; // keys为initialState所有属性名
const initialState = reactive({
  name: "小飞",
});
const actions: MicroAppStateActions & {
  getGlobalState?: Function;
  updateGlobalState?: Function;
} = initGlobalState(initialState);
// 在当前应用监听全局状态,
actions.onGlobalStateChange((newState, prev) => {
  // newState变更后状态,prev变更前状态
  Object.assign(initialState, newState);
  console.log("state..", initialState);
});
​
// 定义一个获取state的方法下发到子应用
actions.getGlobalState = (key?: keys) => {
  return key ? initialState[key] : initialState;
};
//往里面注入一个方法让子元素传递一个版本信息出来。
actions.updateGlobalState = (name: string) => {
  actions.setGlobalState &&
    actions.setGlobalState({
      name,
    });
};
export default actions;
​

子应用child-pro搭建

main.ts 声明各个生命周期

import { createApp, nextTick } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
let instance = null;
(function () {
  if (window.__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  }
})();
function render(props: any = {}) {
  let { container, actions, msg } = props;
  const app = createApp(App);
  // 如果是乾坤环境
  if ((window as any).__POWERED_BY_QIANKUN__) {
    app.config.globalProperties.$actions = actions;
    instance = app
      .use(router)
      .use(ElementPlus)
      .mount(container ? container.querySelector("#app") : "#app");
  } else {
    instance = app.use(ElementPlus).use(router).mount("#app");
  }
}
​
// 独立运行时,直接挂载应用
if (!(window as any).__POWERED_BY_QIANKUN__) {
  render();
}
​
/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap(props: any) {
  console.log("VueMicroApp bootstrap", props);
}
​
/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props: any) {
  console.log("VueMicroApp mount", props);
​
  render(props);
}
​
/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props: any) {
  // console.log("VueMicroApp unmount2222", props);
  // // instance.$destroy();
  instance = null;
}
​

vue.config.js 配置

module.exports = {
  lintOnSave: false,
  publicPath: "/",
  outputDir: "childPro",
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    port: 8889,
  },
  configureWebpack: {
    output: {
      library: `child-pro`,
      libraryTarget: "umd", // 把子应用打包成 umd 库格式
      // <!-- 使用jsonpFunction会报错:configuration has an unknown property ‘jsonpFunction
      // 报错原因:**在2020-10-10发布的webpack 5中已将 output.jsonpFunction
      // 更名为 output.chunkLoadingGlobal
      // -->
      //  jsonpFunction: `webpackJsonp_child-pro`
      chunkLoadingGlobal: `webpackJsonp_child-pro`,
    },
  },
};
​

router.ts 新增路由前缀

const prefix = window.__POWERED_BY_QIANKUN__ ? `/micrApp/child-pro` : "";
const router = createRouter({
  history: createWebHistory(prefix),
  routes,
});

常见问题

  • 子应用需要支持跨域访问

  • vite 搭建的项目暂时不支持qiankun,有插件可以支持但是兼容性不好不建议使用

  • 样式隔离:官网提供了解决方案(shadow dom 和自己实现的 scoped。)但是实施起来有问题,可结合具体项目进行各应用样式隔离如添加样式前缀

  • 数据通信有哪些方式呢?

    1. localStorage/sessionStorage
    2. 通过路由参数共享
    3. 官方提供的 props
    4. 官方提供的 actions:挂载了三个函数(onGlobalStateChange-全局监听状态,setGlobalState-修改全局状态,offGlobalStateChange-移除监听)