主服务 chat
vue3 ts vite 子服务 ppt
react 18 vite 子服务 agent

主服务路由改为history 不然qiankun 的activeRule 匹配不上

const router = createRouter({
  history: createWebHistory(),  // hash history: createWebHashHistory(),
  strict: true,
  routes: constantRouterMap as RouteRecordRaw[],
  scrollBehavior: () => ({ left: 0, top: 0 })
})

主服务

npm install qiankun --save
npm i vite-plugin-qiankun 

mian.ts


import './style/base.scss'
import 'virtual:svg-icons-register'
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'
import Axios from '@/utils/request.js';
import { registerMicroApps, start } from 'qiankun';
// 引入ui 库
// import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

// 引入ui icon


// 将自动注册所有组件为全局组件
// import dataV from '@jiaminghi/data-view'

const app = createApp(App)
// for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
//   app.component(key, component)
// }

app.config.globalProperties.$http = Axios

app.use(createPinia())
app.use(router)
// app.use(ElementPlus)
app.mount('#app')




router/index.ts 不能使用懒加载

    { path: '/ppt', component: ppt },
    { path: '/ppt/home', component: ppt },
    { path: '/agent', component: agent },

agent/index.vue

<template>
  <div
    ref="agentContainer"
    id="agentContainer"
    style="width: 100%;height: 100%;"
  >
    react
  </div>
</template>
<script lang="ts" setup>
</script>
<style>
#pptContainer {
  width: 100vw;
  height: 100vh;
}
</style>

router

    { path: '/ppt', component: ppt },
    { path: '/ppt/home', component: ppt },

ppt/index.vue

<template>
  <div ref="pptContainer" id="pptContainer" style="width: 100%;height: 100%;">
    zzzzzzzzz
  </div>
</template>
<script lang="ts" setup>
})

</script>
<style>
#pptContainer {
  width: 100vw;
  height: 100vh;
}
</style>
</style>

layout/inde.vue

<template>
  <el-container class="layout-container">
    <!-- 顶部导航 -->
    <el-header>
      <Header />
    </el-header>


    <!-- 右侧主区域 -->
    <el-container>


      <!-- 左侧菜单 -->
      <!-- <el-aside  style="height: 100%;"> -->
      <Menu style="height: 100%;" />
      <!-- </el-aside> -->
      <!-- 内容区域 -->
      <el-main>
        <router-view
          v-show="!routeHasQIANKUN"
          v-slot="{ Component }"
          style="height: 100%;"
        >
          <component :is="Component" />
        </router-view>

        <div
          v-show="routeHasQIANKUN"
          style="height: 100%;"
          ref="qiankunContainer"
          id="qiankunContainer"
        >
        </div>
      </el-main>
    </el-container>
  </el-container>
</template>

<script setup lang="ts" name="layout">

defineOptions({
  name: 'layout', // 自定义组件名
});
import Menu from "@/views/menu/index.vue";
import Header from '@/views/header/index.vue';
import { onMounted, ref, watch } from "vue";
import { registerMicroApps, start } from "qiankun";
import { useRoute } from "vue-router";
let routeHasQIANKUN = ref(true)

const route = useRoute();
routeHasQIANKUN.value = route.path.includes('ppt') || route.path.includes('agent')
const qiankunContainer = ref(null);
onMounted(() => {
  registerMicroApps([
    {
      name: 'ppt',
      entry: 'http://localhost:5173/ppt/',
      container: qiankunContainer.value as unknown as HTMLElement, // 老版本这里使用的是 #pptContainer,
      activeRule: '/ppt',
      props: {
        // 传递给子应用的数据
        data: {
          name: 'ppt',
          age: 18,
        },
      },
    },
    {
      name: 'agent',
      entry: 'http://localhost:83/agent/',
      container: qiankunContainer.value as unknown as HTMLElement,
      activeRule: '/agent',
      props: {
        // 传递给子应用的数据
        data: {
          name: 'ppt',
          age: 18,
        },
      },
    }
  ], {
    beforeLoad: [
      (app: any): any => {
        // console.log('before load', app);
        // 挂载前回调
      }
    ],
    beforeMount: [
      (app: any): any => {
        // console.log('beforeMount', app);
        // 挂载后回调
      }
    ],
    afterUnmount: [
      (app: any): any => {
        // console.log('afterUnmount', app);
        // 挂载后回调
      }
    ]
  })
  // setTimeout(() => {
  start({
    prefetch: 'all', // 预加载子应用
    sandbox: { experimentalStyleIsolation: true }, // 样式隔离
  });

  // }, 100);

})
watch(route, (newRoute, oldRoute) => {
  routeHasQIANKUN.value = newRoute.path.includes('ppt') || newRoute.path.includes('agent')
}
)
</script>

<style scoped>
.layout-container {
  height: 100vh;
}

.el-header {
  background-color: blue;
  padding: 0;
  height: 50px;
}

.el-aside {
  background-color: #304156;
  color: white;
  height: calc(100vh - 55px);
}

.el-main {
  background-color: #e9eef3;
  height: calc(100vh - 50px);
  padding: 0 !important;
  margin: 0 !important;
  --el-main-padding: 0 !important;
}
</style>

vue3 ts服务
vite.cong.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun'
import path from 'path';
// import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import viteCompression from 'vite-plugin-compression';
const packName = require('./package').name
// https://vite.dev/config/
export default defineConfig({
  base: '/' + packName + '/',
  plugins: [vue(),
  viteCompression({
    ext: '.gz',  // 输出的压缩文件扩展名
    algorithm: 'gzip',  // 使用 Gzip 算法
    threshold: 10240,  // 仅压缩大于 10KB 的文件
    deleteOriginFile: false,  // 是否删除源文件
  }),
  qiankun(`${packName}`, { // 子应用名称,与package.json的name一致
    useDevMode: true // 开发模式下需要设置为true
  }),
  // createSvgIconsPlugin({
  //   iconDirs: [path.resolve(process.cwd(), './src/assets/svg/')],
  //   symbolId: 'icon-[dir]-[name]',
  // })
  ],
  server: {
    host: "0.0.0.0",
    port: 5173, // 设置子应用端口
    cors: true, // 允许跨域
    headers: {
      'Access-Control-Allow-Origin': '*' // 允许跨域
    }
  },
  resolve: {
    // Vite路径别名配置
    alias: {
      '@': path.resolve('./src'), // @代替src
      'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
    },
  },

  css: {
    preprocessorOptions: {
      scss: {
        // additionalData: '@import "./src/style/index.scss";' // 修复:改为单个字符串
        // charset: false
      }
    }
  },

  // 打包
  build: {
    lib: {
      entry: './src/main.js', // 库的入口文件
      name: 'm-vue', // UMD 格式下挂载到全局的变量名 (window['m-vue'])
      formats: ['umd'], // 指定打包格式
      fileName: (format) => `my-vue-app.${format}.js`
    },
    minify: 'terser',
    chunkSizeWarningLimit: 1500,
    terserOptions: {
      // 清除代码中console和debugger
      compress: {
        drop_console: false,
        drop_debugger: true,
      },
    },
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }
  },
})


main.ts

import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'
import './style/base.scss'
import router from './router/index.ts'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
// const app =  createApp(App)
// app.use(router)
// app.mount('#app')


let instance: any = null

function render(props?: {} | undefined) {
  console.log(props,"pros")
    instance = createApp(App)
    instance.use(router)
    instance.mount('#app')


}
console.log(qiankunWindow.__POWERED_BY_QIANKUN__,"window.__POWERED_BY_QIANKUN__")

// // 独立运行时
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render()
}




renderWithQiankun({
  mount(props: any) {
    render(props)
  },
  bootstrap() {
    // console.log('%c', 'color:green;', ' ChildOne bootstrap')
  },
  update() {
    // console.log('%c', 'color:green;', ' ChildOne update')
  },
  unmount(props: any) {
    instance.unmount()
    instance._container.innerHTML = ''
    instance = null
  }
})

如果报错 app-errors.js:11 Uncaught TypeError: application ‘ppt’ died in status SKIP_BECAUSE_BROKEN: Cannot read properties of undefined (reading ‘instances’) 使用下边这个

import { createApp } from 'vue'
import App from './App.vue'
import './style/base.scss'
import router from './router'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'

let app: ReturnType<typeof createApp> | null = null

function render(props?: { container?: HTMLElement }) {
  const container = props?.container || '#app'
  app = createApp(App)
  app.use(router)
  app.mount(container)
}

// 独立运行逻辑
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render()
}

// 乾坤生命周期
renderWithQiankun({
  bootstrap() {
    console.log('[子应用] bootstrap')
    return Promise.resolve()
  },
  mount(props) {
    console.log('[子应用] mount', props)
    render(props)
  },
  update() {
    console.log('[子应用] update')
  },
  unmount() {
    console.log('[子应用] unmount')
    app?.unmount()
    app = null
  }
})

router/index.ts

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(
    window.__POWERED_BY_QIANKUN__ ? '/ppt' : '/ppt'
  ),
  routes: [
    // 你的路由配置
    {
      path: '/',
      name: 'home',
      component: () => import('../views/home/index.vue'),
    },
    {
      path: '/home',
      name: 'home1',
      component: () => import('../views/home/index.vue'),
    },
  ]
})

export default router

react 18

package.json

    "qiankun": "^2.10.16",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^7.5.2",
    "vite-plugin-qiankun": "^1.0.15"

vite.config.ts

import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
// 1. vite环境下安装qiankun插件
import qiankun from "vite-plugin-qiankun";

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  console.log(mode);  
  const isDev = mode === 'development'
  const env = loadEnv(mode, process.cwd());
  console.log(env); 
  return {
    plugins: [
      // 2. 引用qiankun插件,需要注意,这里的microReact是子应用名称,需要和主应用中注册的子应用名称一致
      qiankun(env.VITE_APP_NAME, {
        useDevMode: true,
      }),
      // 3. react()插件会跟vite-plugin-qiankun插件冲突,所以需要判断是否是开发环境
      !isDev && react(),
    ],
    // 4. 同域配置二级域名,异域配置不需要可修改为:isDev ? "/" : 'http://xxx.com/'
    base: isDev ? "/"+env.VITE_APP_NAME : '/'+env.VITE_APP_NAME,
    server: {
      host: "0.0.0.0",
      port: 83,
      headers: {
        'Access-Control-Allow-Origin': '*'
      },
      proxy: {
        '^/reactApi': {
          target: 'http://localhost:9528/',
          rewrite: path => path.replace(/^\/reactApi/, ''),
          changeOrigin: true
        }
      }
    },
  }
})

.env.development 和 .env.production

VITE_APP_BASE_API='/gateway'
VITE_APP_NAME='agent'

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'

// 1. 引用vite-plugin-qiankun
import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'
let root: ReactDOM.Root | null = null

// 2. qiankun渲染函数
function render(props: QiankunProps) {
  console.log(props,"xxxxxxxxx")
  const { container } = props
  const root = ReactDOM.createRoot(
    (container
      ? container.querySelector('#root')
      : document.querySelector('#root')) as HTMLElement
  )
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  )
  return root
}

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

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

router/index.tsx

// src/router/index.tsx
import React, { Suspense } from 'react'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";

// 路由懒加载
const Home = React.lazy(() => import('../views/home'))
// const List = React.lazy(() => import('../views/list'))
// const NotFound = React.lazy(() => import('../views/notFound'))

export default function Router() {
  return (
    // 同域:判断是否为qiankun引用,给出不同路由;异域分别修改为 '/micro-react' : '/'
    <BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? "/"+import.meta.env.VITE_APP_NAME : "/"+import.meta.env.VITE_APP_NAME}>
      <Routes>
        <Route path='/' element={<Suspense><Home /></Suspense>}></Route>
        <Route path='/home' element={<Suspense><Home /></Suspense>}></Route>
        {/* <Route path='/list' element={<Suspense><List/></Suspense>}></Route> */}
        {/* 定义404路由*/}
        {/* <Route path='/404' element={<Suspense><NotFound/></Suspense>}></Route> */}
        {/* 未匹配的路由使用Navigate重定向到此页面 这里即notFound.jsx */}
        {/* <Route path='/*' element={<Navigate to='/404' />}></Route>  */}
      </Routes>
    </BrowserRouter>
  )
}

转载

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐