qiankun 微前端使用以及注意问题

405 阅读2分钟

📖微前端

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

✨微前端特性

  • 技术栈无关 主框架不限制接入应用的技术栈,子应用可自主选择技术栈
  • 独立开发/部署 各个团队之间仓库独立,单独部署,互不依赖
  • 增量升级 当一个应用庞大之后,技术升级或重构相当麻烦,而微应用具备渐进式升级的特性
  • 独立运行时 微应用之间运行时互不依赖,有独立的状态管理
  • 提升效率 应用越庞大,越难以维护,协作效率越低下。微应用可以很好拆分,提升效率

✈️qiankun 微前端安装以及使用

qiankun官网 (umijs.org)

微前端分为主应用和子应用,主应用和子应用需要单独配置:

主应用部分

  1. 安装 qiankun
yarn add qiankun # 或者 npm i qiankun -S
  1. 注册子应用
import { registerMicroApps, start } from "qiankun";

// 注册子应用
registerMicroApps([
  {
    name: "app1", // 子应用名称
    entry: "//localhost:8080", // 子应用的地址
    container: "#container1", // 子应用需要渲染的dom节点
    activeRule: "/yourActiveRule", // 子应用的匹配
  },
  {
    name: "app2",
    entry: "//localhost:8081",
    container: "#container2",
    activeRule: "/yourActiveRule2",
  },
]);

start();

子应用部分

  1. 导出微应用相关的生命周期钩子

在入口文件中导出 bootstrapmountunmount 三个生命周期钩子,用于主应用调用

/*
bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
  console.log("app bootstraped");
}

// 应用每次进入都会调用 mount 方法,通常我们在这里触发子应用的渲染

export async function mount(props) {
  ReactDOM.render(
    <App />,
    props.container
      ? props.container.querySelector("#root")
      : document.getElementById("root")
  );
}

/*
  应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
  ReactDOM.unmountComponentAtNode(
    props.container
      ? props.container.querySelector("#root")
      : document.getElementById("root")
  );
}

/*
  仅使用 loadMicroApp 方式加载微应用时生效(该生命周期钩子可选)
*/
export async function update(props) {
  console.log("update props", props);
}
  1. 入口文件修改实例渲染方法
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import routes from "./router";
import store from "./store";

if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

let router = null;
let instance = null;

// 将渲染方法抽离
function render(props = {}) {
  const { container } = props; // 从主应用获取在主营中渲染的dom节点
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? "/app-vue/" : "/",
    mode: "history",
    routes,
  });

  instance = new Vue({
    router,
    store,
    render: (h) => h(App),
  }).$mount(container ? container.querySelector("#app") : "#app"); // 根据是否为主应用加载还是独立运行来确定挂载的dom节点
}

// 独立运行时
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() {
  instance.$destroy();
  instance.$el.innerHTML = "";
  instance = null;
  router = null;
}
  1. 配置子应用的打包配置

Vue.config.js

const packageName = require("./package.json").name;

module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: "umd",
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
};

🙋‍♂️注意问题

  • 不同 vue-router 版本路由修改:
// vue-router 3
router = new VueRouter({
  base: window.__POWERED_BY_QIANKUN__ ? "/app-vue1/" : "/",
  mode: "history",
  routes,
});
// vue-router 4
router = VueRouter.createRouter({
  history: VueRouter.createWebHistory(
    window.__POWERED_BY_QIANKUN__ ? "/app-vue2/" : "/"
  ),
  routes,
});
  • 当版本为 webpack5/vue-cli5 的时候这样配置

由于 webpack5 舍弃了 jsonpFunction 字段,需要更改为新的字段 chunkLoading

const { defineConfig } = require("@vue/cli-service");
const name = require("./package.json").name;

module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: "umd", // 把微应用打包成 umd 库格式
      chunkLoading: "jsonp", // webpack5 舍弃了 jsonpFunction 配置,使用chunkLoading
    },
  },
});