qiankun+vite子应用使用指南

831 阅读3分钟

qiankun使用指南

主要记录qiankun如何配置主应用与vite构建的微应用。

前提背景

qiankun是个什么东西

简单来说就是一个把不同应用放到一个容器里面的技术,可以简单的理解为iframe的升级版(当然不可能这么简单)。

qiankun默认支持的是什么

qiankun主应用随便什么都可以,只要能添加qiankun能支持。但微应用默认只能支持webpack打包的,vite是不支持的,想要支持则需要添加插件(至于为什么不支持,这里不多赘述,有很多文档)。

不支持vite的解决办法

vite-plugin-qiankun解决不支持 + vite-plugin-css-injected-by-js解决样式冲突

具体使用

实现目标主应用和微应用打包后,给予同域名进行显示

主应用

安装依赖

yarn add qiankun

添加qiankun主应用的配置文件

import { registerMicroApps } from 'qiankun'
import { IResponseType } from '@/types/api.ts'
interface StartQiankunProps {
  menus: IResponseType['data']
}

const startQiankun = (props: StartQiankunProps) => {
  const defaultProps = {
    token: localStorage.getItem('auth_token'),
    // 这里面就放要传到子组件的参数
  }

  // 1. 要加载的子应用列表
  const micors = [
    {
      name: 'sub', // 子应用的名称
      entry: '//localhost/sub/index.html', // 默认会加载这个路径下的html 这是nginx的路径
      activeRule: '/qiankunSub', // 匹配的路由
      container: '#qiankunSub', // 加载的容器
      props: {
        ...defaultProps
      }
    }
    // 这里可以参照着继续加其他的子应用
  ]

  registerMicroApps(micors, {
    beforeLoad: [
      (app) => {
        console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)
        return Promise.resolve()
      }
    ],
    beforeMount: [
      (app) => {
        console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
        return Promise.resolve()
      }
    ],
    afterUnmount: [
      (app) => {
        console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
        return Promise.resolve()
      }
    ]
  })
}
export default startQiankun

接着要添加给微应用的存放的容器,建议这个容器保持存在,不然删除添加会导致微应用不断卸载加载,极其浪费,甚至产生各种bug

<div id='sub'></div>

在确保容器存在后,启动qiankun

import startQiankun from '@/utils/qiankunConfig.ts'
// 确保容器渲染完成后启动 qiankun
startQiankun({
	//这里可放你要传的数据
}) // startQiankun可以提前执行,放哪随意,看法业务逻辑
  
if (document.querySelector('#sub')) {
  start();
}

微应用(特指vite构建的)

添加依赖

yarn add vite-plugin-qiankun vite-plugin-css-injected-by-js

在vite.config.ts上配置

import qiankun from 'vite-plugin-qiankun'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
export default defineConfig(({ mode }) => {
  return {
   // 当然你可以这样配置 base: VITE_APP_ENV === 'production' ? '/' : '/',
    base: '/sub',  // 指向的是nginx要存放的路径地址 -> 主应用下的/sub
    plugins: [
      react(),
      qiankun('qiankunSub', { // 这是主应用要指向的子应用名字
        useDevMode: true
      }),
      cssInjectedByJsPlugin(),
    ],
  }
})

改造main.tsx

import { createRoot, Root } from 'react-dom/client'
import App from './App'
import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'
import QiankunProvider from '@/components/QiankunProvider'


declare global {
  interface Window {
    QIANKUN__MANAGER?: boolean
  }
}
// 用于保存 React 根实例
let app: Root | null = null

// 初始化 Qiankun 子应用
const initQianKun = () => {
  renderWithQiankun({
    // 子应用初始化钩子
    bootstrap() {
      console.log('微应用:bootstrap')
    },
    // 子应用挂载钩子
    mount(props) {
      console.log('微应用:mount', props)
      render(props) // 调用渲染函数
    },
    // 子应用卸载钩子
    unmount(props) {
      console.log('微应用:unmount', props)
      if (app) {
        app.unmount() // 卸载 React 应用
        app = null
      }
    },
    // 子应用更新钩子
    update(props) {
      console.log('微应用:update', props)
    }
  })
}

// 渲染函数,根据是否传入容器决定渲染位置
function render(props: QiankunProps) {
  const { container } = props
  const rootContainer = container
    ? container.querySelector('#root') // 使用主应用传入的容器
    : document.getElementById('root') // 默认容器

  if (!rootContainer) {
    console.error('无法找到 #root 容器')
    return
  }

  // 如果未创建过 app 实例,则创建
  if (!app) {
    app = createRoot(rootContainer)
  }

  // 渲染主应用
  app.render(
    <QiankunProvider {...props}>
      <App />
    </QiankunProvider>
  )
}

// 如果不是通过 Qiankun 加载的独立运行模式
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  // window.QIANKUN__MANAGER = true
  render({}) // 直接渲染主应用
} else {
  window.QIANKUN__MANAGER = qiankunWindow.__POWERED_BY_QIANKUN__
  initQianKun() // 启动 Qiankun 子应用模式
}

套一个 QiankunProvider组件用来处理主应用传过来的props,并传递下去

import { createContext, useEffect, useState } from 'react'
import { QiankunProps } from 'vite-plugin-qiankun/dist/helper'
import { cloneDeep } from 'lodash'

export const QiankunContext = createContext<QiankunProps>({
  token: '',
})

const QiankunProvider = (props: QiankunProps) => {
  const [data, setData] = useState(props)

  const handleInit = () => {
    const newData = cloneDeep(props)
    // 这里就可以处理主应用传入的props了
    setPostData(newData)
  }

  useEffect(() => {
    if (window.QIANKUN__MANAGER) {
      handleInit()
    }
  }, [])

  return <QiankunContext.Provider value={{ ...data}}>{props.children}</QiankunContext.Provider>
}

export default QiankunProvider

如此,一切就搞定了,之后就根据业务需求添加相关东西

nginx

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {

        listen 80;

        # 主应用
        location / {
            root html\dist;  # 主应用路径
            index index.html;
            try_files $uri /index.html; 
        }

        # 子应用1
        location /qiankunSub {
            root html\dist\sub;  # 子应用路径
            index index.html;
            try_files $uri /index.html;
        }
    }
}

就此搞定