• 作者:老汪软件技巧
  • 发表时间:2024-10-01 07:01
  • 浏览量:

什么是 Qiankun?

简单来说,Qiankun 是一个基于 single-spa 的微前端实现库。它让多个前端应用在一个页面上无缝运行。想象一下,你有一个大盒子,里面可以放很多小盒子,每个小盒子都是一个独立的应用,但它们又能很好地协作,就像一个现代的“橡皮泥”框架。

Qiankun 的核心是提供了一种新的应用架构思路,方便我们更好地拆分前端项目,让每个子应用都能单独开发和部署。

Qiankun 能干啥?

随着业务量增长,单一的前端项目会变得难以维护:

Qiankun 帮助我们:

那为啥不用iframe来个实战

OK,现在我们来完成qiankun的示例项目,一起了解如何接入qiankun框架。这是我们所使用的项目框架:

主应用 by Vue3

// qiankun
import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun'
  
// 注册子应用
registerMicroApps([
    // VUE3子应用
    {
      name: 'subAppVue3',
      entry: 'http://localhost:3002',
      container: '#subapp-vue3',
      // 当路由中包含有subv3时,激活子应用
      activeRule: location => {
        return location.pathname.includes('/subv3')
      },
    },
    // react子应用
    {
      name: 'subAppReact',
      entry: 'http://localhost:3003',
      container: '#subapp-react',
      // 当路由中包含有subreact时,激活子应用
      activeRule: location => {
        return location.pathname.includes('/subreact')
      },
    },
  ])
  // 启动 qiankun
  start({
    prefetch: 'all',
  })
  // 添加全局异常捕获(非必须)
  addGlobalUncaughtErrorHandler(handler => {
    console.log('qiankun异常捕获', handler)
  })

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/subv3',
      name: 'subv3',
      component: SubAppView
    },
    {
      path: '/subreact',
      name: 'subreact',
      component: SubAppView
    },
  ]
})



要注意的是,这里配置的渲染子应用的容器ID,要跟main.ts配置的子应用容器保持一致。


Vue3子应用

对于子应用来说,我们不仅要考虑子应用被嵌入口主应用运行,还需要考虑单独运行要如何处理。这里同样为了方便,我们仍然先使用vite脚手架创建一个全新的项目,在此基础上进行修改。

一般情况下,vue的app在创建后都是直接挂载在#app DOM进行渲染,但是作为子应用,我们必须要把渲染逻辑交给qiankun框架来完成:

// main.ts
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
const initQianKun = () => { 
    renderWithQiankun({
        bootstrap() {console.log('bootstrap');},
        mount(_props) {
            console.log('mount', _props);
            render(_props.container)
        },
        unmount(_props) {
            console.log('unmount', _props);
        },
        update: function (props) {console.log('update');}
    })
}
const render = (container: HTMLElement | null | undefined) => {
    const app = createApp(App)
    const appDom = container ? container : "#sub-app-v3"
    app.mount(appDom)
}
// 判断是否为乾坤环境,否则会报错iqiankun]: Target container with #subAppContainerVue3 not existed while subAppVue3 mounting!
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render(null)

这里进行运行环境判断,如果是子应用环境,则调用qiankun来进行app的渲染,否则则直接渲染。

// qiankun插件
import qiankun from 'vite-plugin-qiankun'
export default defineConfig({
  plugins: [
    vue(),
    qiankun('subAppVue3', { useDevMode: true })
  ],
  server: {
    port: 3002,
    host: true
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

这样,就完成了对vue3子应用的改造,如果现在直接启动子应用,则会成功看到页面效果:

react子应用

react的改造步骤稍微多一些

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

import './public-path'
import './index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
export function render(props) {
  const { container } = props;
  const conDom = container ? container.querySelector('#react-root') : document.getElementById('react-root');
  console.log("conDom", conDom)
  const root = ReactDOM.createRoot(conDom);
  root.render(
    <React.StrictMode>
      <App />
    React.StrictMode>
  );
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}
export async function bootstrap() {
  console.log('React app bootstrapped');
}
export async function mount(props) {
  console.log('Props from main framework', props);
  render(props);
}
export async function unmount(props) {
  const { container } = props;
  ReactDOM.unmountComponentAtNode(container ? container.querySelector('#react-root') : document.getElementById('react-root'));
}

跟vue3子应用类似,我们也需要处理直接渲染react的逻辑,同时将qiankun的各生命周期函数暴露出来,供qiankun进行调用。

pnpm i -D @rescripts/cli

const { name } = require('./package.json');
module.exports = {
  webpack: (config) => {
    config.output.library = `${name}-[name]`;
    config.output.libraryTarget = 'umd';
    config.output.globalObject = 'window';
    return config;
  },
  devServer: (_) => {
    const config = _;
    config.headers = {
      'Access-Control-Allow-Origin': '*',
    };
    config.historyApiFallback = true;
    config.hot = false;
    config.liveReload = false;
    return config;
  },
};

同时把启动脚本修改为rescripts

{
    "start": "PORT=3003 rescripts start",
    "build": "rescripts build",
    "test": "rescripts test",
    "eject": "rescripts eject"
  }

直接启动react项目,一切正常的话可以看下运行效果

整体运行效果

同时启动三个项目,然后通过切换路由来确认子项目是否都能成功加载:

示例源码

/open4jj/qia…

示例源码放在gitee上了,有需要的同学请自取,欢迎一键三连!