vite2 接入 qiankun 微前端

1,811 阅读3分钟

关注公众号: 微信搜索 前端工具人 ; 收货更多的干货

vite 已经大火了, 自从用上了就停不下来` ;

但碍于 vite 初来乍到, 生态不够成熟, 有些功能还是比较尴尬的,比如微前端;

qiankun 微前端搭建详细步骤及常见问题,可参考之前文章 juejin.cn/post/694376…

以下源码也已上传至 github

开篇

去年初项目接入微前端的时候,就准备使用 vite, 一顿构操作下来,发现 vite 应用一直无法获取生命周期, 才知道那时 qiankun 暂时不支持 vite 应用

大致原因如下:

  • vite 构建的 js 内容必须在 type=modulescript 脚本里;
  • 当时qiankun 的源码依赖之一 import-html-entry 则不支持 type=module 这个属性 (目前已支持);
  • qiankun 是通过 eval 来执行这些 js 的内容,而 vite 里面 import/export 没有被转码, 所以直接接入会报错:不允许在非type=modulescript 里面使用 import;

在去年年尾的时候,在qiankunissues 中找到了2种方法;

还是这些大佬牛逼;下面介绍下这2种方法

方法一 (不推荐)

  • 结合 rollup/plugin-html 插件;
  • 修改Vite.config.js中的build配置默认Vite的输出目标targetmodule,改为esnext
  • 详细步骤可参考 https://github.com/umijs/qiankun/issues/1268

缺点:

  • 可以实现生产环境接入,开发环境不行;
  • vite没有动态publicPath的支持;所以 Vite.configbase 配置需要写死;
  • vite code-splitting(代码分割)功能并不支持iifeumd两种格式,导致路由无法懒加载;
  • 图片资源只会被打包成 base64,无论图片大小

方法二 (推荐)

vite-plugin-qiankun 插件; github文档

优点

  • 保留 vite 构建 es 模块的优势
  • 一键配置,不影响已有的 vite 配置
  • 支持 vite 开发环境
  1. 安装插件

pnpm add vite-plugin-qiankun

  1. vite.config.ts 配置
// vue3
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun';
import { resolve  } from 'path';
export default ({ mode }) => {
  const __DEV__ = mode === 'development'
  return defineConfig({
    alias: {
      '@': resolve('src'),
    },
    server: {
      port: 7711,
      origin: '//localhost:7711'
    },
    base: __DEV__ ? '/' : '//localhost:7711',
    plugins: [ vue(),
      qiankun('sub-vite2-vue3', {
        useDevMode: true
    })],
  })
}
// react
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import qiankun from 'vite-plugin-qiankun';
import reactRefresh from '@vitejs/plugin-react-refresh'
// useDevMode 开启时与热更新插件冲突
const useDevMode = true
export default ({ mode }) => {
  const __DEV__ = mode === 'development'
  return defineConfig({
    server: {
      port: 7722,
      origin: '//localhost:7722'
    },
    base: __DEV__ ? '/' : '//localhost:7722',
    plugins: [
      ...(
        useDevMode ? [] : [
          reactRefresh()
        ]
      ),
      qiankun('sub-vite2-react', {
        useDevMode: true
      })
    ],
  })
}
  1. 导出相应的生命周期
// vue3 main.ts  无关代码自行省略
// @ts-nocheck
import { createApp } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import App from "./App.vue";
import routes from "./router";
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

let router = null;
let instance = null;

renderWithQiankun({
  mount(props) {
    storeTest(props);
    render(props);
    instance.config.globalProperties.$onGlobalStateChange =
      props.onGlobalStateChange;
    instance.config.globalProperties.$setGlobalState = props.setGlobalState;
  },
  bootstrap() {
    console.log("%c ", "color: green;", "sub-vite2-vue3 app bootstraped");
  },
  unmount(props: any) {
    instance.unmount();
    instance._container.innerHTML = "";
    instance = null;
    router = null;
  },
});

function render(props = {}) {
  const { container } = props;
  router = createRouter({
    history: createWebHistory(!qiankunWindow.__POWERED_BY_QIANKUN__ ? "/sub-vite2-vue3" : "/"),
    routes
  });

  instance = createApp(App);
  instance.use(router);
  instance.mount(container ? container.querySelector("#app") : "#app");
}

function storeTest(props) {
  props.onGlobalStateChange &&
    props.onGlobalStateChange(
      (value, prev) =>
        console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
      true
    );
  props.setGlobalState &&
    props.setGlobalState({
      ignore: props.name,
      user: {
        name: props.name
      }
    });
}
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render({});
}
// react  main.ts  无关代码自行省略
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

// vite-plugin-qiankun helper
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

function render(props: any) {
  const { container } = props;
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    container
      ? container.querySelector("#root")
      : document.getElementById("root")
  );
}

renderWithQiankun({
  mount(props) {
    console.log("sub-vite2-react mount");
    render(props);
  },
  bootstrap() {
    console.log("bootstrap");
  },
  unmount(props: any) {
    console.log("sub-vite2-react unmount");
    const { container } = props;
    const mountRoot = container?.querySelector("#root");
    ReactDOM.unmountComponentAtNode(
      mountRoot || document.querySelector("#root")
    );
  },
});

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render({});
}

如发现子应用图片资源 404 , 控制台报错显示该资源在主应用路劲下;则 vite.config.ts 增加 origin

...
server: {
  ...
  origin: '//localhost:7722'
},

参考资源:

  1. github.com/umijs/qiank…
  2. github.com/tengmaoqing…