likes
comments
collection
share

微前端qiankun+vite+Vue3+React 实践指南

作者站长头像
站长
· 阅读数 59

前言

因为之前刚好有好几个项目因为项目比较大,子模块应用也很复杂,部分子模块要求能够独立运行独立部署,而且后期可能还会有其他子模块持续接入,所以经过技术分析选型,最后用到了 微前端框架qiankun,在实际过程中体验还可以,所以准备写下这篇文章进行一些总结分享。

  • 什么是微前端

1.每个子应用可以独立开发、独立部署,不影响其他子应用。

2.各子应用可以使用不同的前端技术栈(如 React、Vue 等)。

3.通过拆分大型项目,降低单个应用的复杂度,提升代码的可维护性。

4.每个子应用之间状态隔离,运行时状态不共享。

1.qiankun 是基于 single-spa 的微前端解决方案。

2.由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。

3.qiankun 支持 样式隔离,确保微应用之间样式互相不干扰。

4.qiankun 支持资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

5.qiankun 已经发布多年,技术比较成熟稳定,文档齐全。

准备工作

  • 因为最近一直用Vue3,所以创建一个vue3+vite+router+element-plus项目app-main作为主应用 主要作用是:用来做统一的登录、子应用菜单切换、个人信息提示、消息通知提示等。

微前端qiankun+vite+Vue3+React 实践指南

  • 创建一个vue3+vite+router+element-plus项目app-first作为子应用1,运行起来效果如下。

微前端qiankun+vite+Vue3+React 实践指南

  • 创建一个react+vite+router+antdv项目app-second作为子应用2,运行起来效果如下。

微前端qiankun+vite+Vue3+React 实践指南

  • 项目目录结构大致如下,我把代码上传到了 gitee 上,有需要的掘友可以下载下来,跟着后面思路一起动手试一试。

微前端qiankun+vite+Vue3+React 实践指南

开始修改配置qiankun

1.修改主项目
  • 添加 qiankun 到主项目中。
npm install qiankun
  • 在 src 目录下面创建 /common/micro-app.ts
export const microApps = [
  {
    name: 'first', // 
    entry:"//localhost:7001", // // import.meta.env.MODE === "dev" ? "//localhost:7002" : "/main/second/",
    container: '#page-main-container', // 挂载 子应用的 容器Id
    activeRule: '/main/first',
  
  },
  {
    name: 'second', // 
    entry:"//localhost:7002", // import.meta.env.MODE === "dev" ? "//localhost:7002" : "/main/second/",
    container: '#page-main-container',// 挂载 子应用的 容器Id
    activeRule: '/main/second',
  },
];

  • 修改主应用 router 利用 :pathxx(.*)* 去匹配所有路由
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
  {
    path: '/',
    redirect: '/main/first',
  },
  {
    path: "/login",
    name: "Login",
    component: () => import("@/views/login.vue"),
  },
  {
    path: "/main/:pathxx(.*)*", //通配所有路由
    name: "Main",
    meta: {
      title: '根路由',
    },
    component: () => import(/* webpackChunkName: "main" */ '@/views/index.vue'),
  },

];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

  • 修改 src/views/index.vue 注册启动 qiankun 微应用 详细参数配置可以参考下qiankun官方文档
<template>
    <el-container>
        <el-header style="padding: 0;">
            <el-menu class="el-menu-demo" mode="horizontal" background-color="#545c64" text-color="#fff"
                active-text-color="#ffd04b"   @select="handleSelect">
                <el-menu-item index="/main/first" @clikc="gotoUrl('/main/first')">子应用1</el-menu-item>
                <el-menu-item index="/main/second" @clikc="gotoUrl('/main/second')">子应用2</el-menu-item>
            </el-menu>
        </el-header>
        <div id="page-main-container">
            <RouterView></RouterView>
        </div>
    </el-container>
</template>
<script setup lang="ts">
import { onBeforeMount } from 'vue'
import { microApps } from "@/common/micro-app.ts"
import {
    registerMicroApps,
    start
} from "qiankun";
const handleSelect=(key:string)=>{
    gotoUrl(key)
}
const gotoUrl=(url:string)=>{
    console.log('跳转',url)
    history.pushState(null, url, url);
}
onBeforeMount(() => {
    const windowPbj: any = window
    if (!windowPbj.qiankunStarted) {
        registerApp();
        start();
    }

})

// 挂载应用
const registerApp = () => {
    registerMicroApps(microApps)
}

</script>
2.修改子应用1 (vue3项目)
npm install vite-plugin-qiankun -D
  • 修改子应用的 vite.config.ts 这里需要注意名称要和主应用里面micro-app.ts注册的 name保持一致
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
    qiankun('first', {
      // 微应用名字,与主应用注册的微应用名字保持一致
      useDevMode: true,
    }),
  ],
  resolve: {
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, './src'),
      },
    ],
  },
  server: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
    host: "0.0.0.0", // ip
    port: 7001,
    origin: "http://localhost:7001",
   
  },
})

  • 因为采用了 history 模式 所以需要修改一下src/routers/index.ts router 的入口配置,需要注意入口名称和主应用micro-app.ts 配置中 activeRule 保持一致。
import { createRouter, createWebHistory } from 'vue-router';
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
const routes = [
  {
    path: '/',
    name: 'Layout',
    redirect: '/home',
    component: () => import(/* webpackChunkName: "home" */ '@/Layout/index.vue'),
    meta: {
      title: '根路由',
    },
    children: [
      {
        path: '/home',
        name: 'Home',
        component: () => import(/* webpackChunkName: "home" */ '@/views/index.vue'),
        meta: {
          title: '首页',
        },
      },

    ],
  }

];
export const history = createWebHistory(
  qiankunWindow.__POWERED_BY_QIANKUN__ ? '/main/first' : "/"
);

const router = createRouter({
  history: history,
  routes,
});

export default router;

  • 修改子应用 main.ts,主要是用qiankunWindow.__POWERED_BY_QIANKUN__判断环境,利用 renderWithQiankun 注册qiankun, 主要代码如下。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router, { history } from "@/routers/index";
import {
    renderWithQiankun,
    qiankunWindow
} from 'vite-plugin-qiankun/dist/helper';

let instance: any = null;

async function render(props: any = {}) {
    const { container, activeRule } =
        props;

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

const initQianKun = () => {
    renderWithQiankun({
        mount(props) {
            render(props);
        },
        bootstrap() { },
        update() { },
        unmount() {
            instance.unmount();
            instance._container.innerHTML = "";
            instance = null;
            history.destroy();
        },
    });
};

if (qiankunWindow.__POWERED_BY_QIANKUN__) {
    console.log("qiankun渲染开始");
    initQianKun();
} else {
    console.log("不是qiankun渲染");
    render();
}

运行效果如下

微前端qiankun+vite+Vue3+React 实践指南

3.修改子应用2 (React项目)
  • 因为用到了vite框架所以推荐直接用封装好了的插件 vite-plugin-qiankun,因为它保留了 vite 构建 es 模块的优势。
npm install vite-plugin-qiankun -D
  • 修改子应用 vite.config.ts 配置,加载vite-plugin-qiankun 插件,因为会和 @vitejs/plugin-react插件冲突 所以要判断环境去掉 @vitejs/plugin-react 插件。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'node:path';
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  const isDev = mode === 'development'
  return {
    plugins: [
    // second 要和主应用注册子应用时的名称一样(registerMicroApps的name属性)
      qiankun("second", {
        useDevMode: true,
      }),
      !isDev && react(),
    ],
    resolve: {
      alias: [
        {
          find: '@',
          replacement: resolve(__dirname, './src'),
        },
      ],
    },
    server: {
      headers: {
        "Access-Control-Allow-Origin": "*",
      },
      host: "0.0.0.0", // ip
      port: 7002,
      origin: "http://localhost:7002",
     
    },
  };
});

  • 修改 main.tsx 判断环境利用renderWithQiankun挂载注册qiankun。原理和Vue 项目挂载逻辑原理其实差不多,只是具体渲染代码有所不同。
// import './public-path';
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import {
  renderWithQiankun,
  qiankunWindow,
  QiankunProps,
} from 'vite-plugin-qiankun/dist/helper'
let root: ReactDOM.Root | null = null

function render(props: QiankunProps) {
  const { container } = props
  const root = ReactDOM.createRoot(
    (container
      ? container.querySelector('#root')
      : document.getElementById('root')) as HTMLElement
  )
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
  return root
}

const initQianKun = () => {
  renderWithQiankun({
    mount(props) {
      root = render(props)
    },
    bootstrap() {
      console.log('bootstrap')
    },
    unmount(props) {
      root?.unmount()
    },
    update(props) {
    },
  })
}

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  root = render({})
} else {
  initQianKun();
}


  • 修改 App.tsx,最主要的内容是修改 react-router-dom路由组件BrowserRouter 属性 basename,原理和修改Vue-router createRouter 差不多,都是为了修改入口配置用来匹配qiankun。
import { Suspense } from "react"
import { BrowserRouter, Routes, Route,Navigate  } from "react-router-dom";
import routes, { RouterType } from "./routers"; // Import your route configuration
import React from "react";
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
function App() {
  // 由于路由组件是懒加载的,渲染页面可能会有延迟,使用Suspense 可优化交互
  const RouteEleMent = (route: RouterType): React.ReactNode => {
    if (!route.component) {
      return null;
    }
    return (
      <Suspense fallback={<div> Loading...</div>}>
        <route.component />
      </Suspense>
    );
  };
  // 通过每个路由对象渲染Route
  const RouteItem = (route: RouterType) => {
    
    return (
      <Route key={route.name} element={RouteEleMent(route)} path={route.path}>
        {RouteList(route.children ?? [])}
      </Route>
    );
  };
  // 根据配置的routeconfig 生成Route
  const RouteList = (list: RouterType[]) => {
    return list.map((item) => RouteItem(item));
  };

  return (
    <div className="App">
      <BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? "/main/second" : "/"}>
          <Routes>
            {/* 重定向 */}
           <Route path="/" element={<Navigate to="/home" />} />
            {RouteList(routes)}
          </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;


运行效果如下

微前端qiankun+vite+Vue3+React 实践指南

总结

通过上面文章,我们成功地使用 Vue 3 和 Vite 创建了主框架,并集成了 qiankun 来管理和加载 React 18 和 Vue3 子应用。由于篇幅有限,所以这里重点是使用qiankun的应用部署,为了方便大家实践理解,我把源代码放在了gitee上 有需要的小伙伴可以自己下载下来体验一下,如果有遇到问题欢迎评论区或者私信我。

转载自:https://juejin.cn/post/7390689983790104591
评论
请登录