浅入浅出,微前端qiankun

2,009 阅读4分钟

本文主要是记录在公司使用 qiankun 遇到的问题,和使用的心得,不是教程

在说qiankun前,先简单介绍下微前端

  • 微前端是什么
  • 由不同独立发布的系统共同构建web的方法
  • 为什么情况下用微前端
    • 项目需要独立部署,并被其他系统集成
    • 长期大项目,需要不断迭代
  • 为什么选择qiankun
    • 国内使用的人多,相比较 single-spa,开箱即用
  • 美中不足
    • 原生不支持vite
    • 缺少较好的组件共享方案
    • MicroApp 比较,接入qiankun的改动会更多

如何使用qiankun

基座的改动 (只有基座需要引入qiankun)

yarn add qiankun

//src/index  入口文件
    import { registerMicroApps, start } from 'qiankun';

    export const app: RegistrableApp<any>[] = [
      {
        name: 'micro', //微应用名称
        entry:`http://localhost:9000/`, // 微应用地址
        container: '#container', // 基座会在这个DOM下渲染微应用
        activeRule: '/micro', //触发路径,需要和微应用router的basename相同
        props: { store }, // 把基座的redux传递给微应用,微应用就可以使用如token、userinfo等数据
      },
      
    ];
    // 注册微应用
    registerMicroApps(app);
    // 渲染微应用
    start()

React微应用的改动主要有4个点

  1. public地址

微应用修改__webpack_public_path__(webpack动态public)。不修改这个,主应用访问微应用时,资源路径会有问题

src目录下创建public-path.js

// src/public-path.js 

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

在入口index文件中引入

// src/index.tsx
import './public-path'; //一定要在入口文件顶部引入

  1. 入口文件生命周期

微应用启动生命周期,主应用触发url后调用对应的微应用

// src/index.tsx
// activeRule 为注册微应用时候的地址

// 根据qiankun环境变量,单独运行微应用
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}

// 仅首次匹配activeRule执行,在这里执行render
export async function bootstrap() {}

// 每次匹配activeRule就会执行
export async function mount(props) {

  const parentDva = props?.store;
  if (parentDva) {
    // 这里拿到的基座的store,里面有的登录后的token,用户信息等数据
    render(props);
  } 
  
}

// 每次离开activeRule就会卸载
export async function unmount() {
    const { container } = props;
    ReactDOM.unmountComponentAtNode(
        container
        ? container.querySelector('#root')
        : document.querySelector('#root'),
}

  1. Router的basename属性 修改子应用basename
  // 根据qiankun环境变量决定pathname
    <Router basename={window.__POWERED_BY_QIANKUN__ ? '/micro' : '/'}>
      <Routers />
    </Router>
  1. 开启webpack打包跨域,修改打包后的格式为UMD

开启跨域,更改成umd格式

  // config-overrides.js
   (config) => {
    // 修改打包格式为UMD
      config.output.library = `${packageName}-[name]`;
      config.output.libraryTarget = 'umd';
      config.output.chunkLoadingGlobal = `webpackJsonp_${packageName}`;
      config.output.globalObject = 'window';
      return config;
    }
  devServer: overrideDevServer((config) => {
   // 修改CORS开启跨域,让基座能通过fetch加载微应用资源
    config.headers = config.headers || {};
    config.headers['Access-Control-Allow-Origin'] = '*';
    return config;
  }, watchAll()),

改造完成

qiankun Options配置

sandbox样式隔离

  • strictStyleIsolation 通过Shadow DOM来隔离,3.0计划删除,会隔离样式和事件,引出更多的问题
  • experimentalStyleIsolation 命名空间,给微应用增加类名前缀,但主应用对微应用的样式影响依旧存在
    • 主应用样式依旧会影响微应用

    • 微应用一些挂在body的组件样式会失效,比如antd的modal,Select,但是可以在ConfigProvider配置挂载DOM

    笔者这里两种都没有选择,因为experimentalStyleIsolation会导致公司某些旧组件下拉框出现问题,示意图选择了修改antd的前缀,让几个应用前缀不同
// App.tsx
ConfigProvider.config({
 prefixCls: 'base-ant',
});
// config-overrides.js
   addLessLoader({
     lessLoaderOptions: {
       lessOptions: {
         modifyVars: {
           'ant-prefix': 'base-ant',
         },
         javascriptEnabled: true,
       },
     },
   }),

JS沙箱 (保持微应用window的隔离)

  • snapshotSandbox

    只能一个微应用,因为要遍历window的属性所以性能不好

  • proxySandbox

    支持多微应用,需要proxy

  • legacySandbox(3.0计划删除)

    只能一个微应用,需要proxy,但是性能比snapshotSandbox更好

遇到的问题

微应用主应用通信

主应用传递dva-core给微应用,从store拿数据,比如登录后的token和userinfo

微应用发版后缓存

微应用的http缓存,微应用发版之后,chunk文件会变化,但是index.html是没有的,所以会加载改变之前的chunk文件

我的做法是nginx给微应用的index.html加上了Cache-Control: no-store,no-Cache,这样js、css还是能够缓存,也避免了chunk加载错误的问题

路由跳转

微应用A想跳转到微应用B,用自己的router跳转,会带上basename

  • 主应用传递router给微应用,再用这个router跳转
  • 使用history,或者history.js跳转

获取微应用的信息

场景是我需要在加载微应用前就获取到微应用的路由信息,才能提前生成菜单

因为微应用已经允许跨域,这里我采用的是基座直接fetch微应用public下的json文件

image.png

// url是微应用的地址
fetch(`${url}/routerConfig.json`)
  .then(async (data) => {
    const json = await data.json();
    if (json) {
     // 拿到了json根据json去生成菜单,这里代码省略
    }
})

微应用部署时怎么部署到一个端口

就我希望基座/base 微应用/base/child/micro github.com/umijs/qiank… 这里附上 gongshun大佬给的解决方案