写一个无界微应用的demo vite+vue3

34 阅读2分钟

解决、el-select 位置偏移以及 el-table tootip 位置偏移的问题 el中依赖了poper.js 的fixed定位偏移,子应用的dom挂载在shadowRoot上,导致计算错误 Desktop/project/code/web/js/2025/微前端/WuJie/my-analysis

父应用

mian.js

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

import WujieVue from "wujie-vue3";

import { initApplication } from './wujieConfig/application'

initApplication()
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.use(WujieVue);
app.mount('#app')

application.js

import WujieVue from 'wujie-vue3'
import { APPLICATION_NAME, getApplicationHost } from './constants'
import lifecycles from "../lifecycle";
import credentialsFetch from "../fetch";
const { setupApp, preloadApp, bus } = WujieVue
window.request = ()=> new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({
      data: {
        code: 200,
        message: 'success',
        list:[1,2,3]
      }
    })
  }, 1000);
})
window.BUS = bus
const props = {
  jump: (name) => {
      router.push({ name });
  },
};
export function initApplication() {
  /**
   * 配置应用
   * 给子应用注入父应用的window的某些能力,比如request请求都使用父亲的
      */
  const plugins = [
    {
      jsBeforeLoaders: [
        {
          content: `window.request=window.parent.request;
          window.BUS=window.parent.BUS;`,
        },
      ],
    },
  ]
  const loadingElm = document.createElement('div')
  loadingElm.innerHTML = ''
  setupApp({
    name: APPLICATION_NAME.ANALYSIS,
    url: getApplicationHost(APPLICATION_NAME.ANALYSIS),
    exec: true,
    props,
    alive: true,
    lifecycles,
    plugins,
  })

my-base/src/wujieConfig/constants.js

// 开发环境域名
export const DEVELOPMENT_MAP = {
  analysis: '//localhost:9232',
}
const origin = window.location.origin

export const getApplicationHost = (applicationName) => {
  const env = import.meta.env.VITE_ENV_NAME
  switch (env || 1) {
    case 'development':
      return DEVELOPMENT_MAP[applicationName]
}



my-base/src/router/index.js


import { createRouter, createWebHistory } from 'vue-router'
import EmptyLayout from '../views/emptyLayout.vue'


const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      redirect: '/page01',
    },
    {
      path: '/page01',
      name: 'page01',
      component: () => import('../views/Page01.vue'),
    },
    {
      path: '/danalyse/:pathMatch(.*)*',
      name: 'danalyse',
      component: () => import('../views/danalyse/index.vue'),
    },
    
    {
      path: '/mcenter',
      name: 'mcenter',
      component: EmptyLayout,
      children: [
        {
          path: 'project01',
          name: 'project01',
          component: () => import('../views/mcenter/project01.vue'),
        }, 
        {
          path: 'project02',
          name: 'project02',
          component: () => import('../views/mcenter/project02.vue'),
        }, 
      ]
    },
    
  ],
})

export default router


my-base/src/App.vue

<template>
  <div style="display: flex; flex-direction: row">
    <el-menu
      :default-active="route.path"  
      class="el-menu-vertical-demo"
      :collapse="isCollapse"
      @open="handleOpen"
      @close="handleClose"
      router
    >
      <!-- 菜单内容保持不变 -->
      <el-menu-item index="/">
        <el-icon><location /></el-icon>
        <span>第一页</span>
      </el-menu-item>

      <el-sub-menu index="danalyse">
        <template #title>
          <el-icon><location /></el-icon>
          <span>分析</span>
        </template>
        <el-menu-item index="/danalyse/event">事件分析</el-menu-item>
        <el-menu-item index="/danalyse/session">session分析</el-menu-item>
      </el-sub-menu>

      <el-sub-menu index="mcenter">
        <template #title>
          <el-icon><location /></el-icon>
          <span>管理中心</span>
        </template>
        <el-menu-item index="/mcenter/project01">项目管理</el-menu-item>
        <el-menu-item index="/mcenter/project02">权限管理</el-menu-item>
      </el-sub-menu>
    </el-menu>
    <div style="flex: 1">
      <RouterView />
    </div>
  </div>
</template>

<script setup>
import { RouterView } from "vue-router";
import { useRoute, useRouter } from "vue-router";  // 导入useRoute
import { ref,watch } from "vue";
import { Location } from "@element-plus/icons-vue";
import WujieVue from 'wujie-vue3'

const { bus } = WujieVue
// 获取当前路由实例
const route = useRoute();
const router = useRouter();

const isCollapse = ref(false);
const handleOpen = (key, keyPath) => {
  console.log(key, keyPath);
};
const handleClose = (key, keyPath) => {
  console.log(key, keyPath);
};

// fuxing: 父路由变化了,通知子应用
watch(
  () => route.fullPath,
  () => {
    console.log("fuxing: 父应用 路由变化了,通知子应用  " , route.fullPath);
    bus.$emit('baseRouteChange', route)
  },
  {
    immediate: true,
  }
)

// 子应用修改切换子路由时,通知同步路由
bus.$on("sub-route-change", (name, path) => {
    const mainName = `${name}`;
    const mainPath = `/${name}${path}`;
    
    // const currentName = router.currentRoute.value.name;
    const currentPath = router.currentRoute.value.path;
    // console.log('fuxing:tag',mainName , currentName , mainPath , currentPath);
    // if (mainName === currentName && mainPath !== currentPath) {
    //   console.log('fuxing: sub-route-change ',mainPath);
    //   router.push({ path: mainPath });
    // }
    console.log('fuxing: 子应用发送消息',"mainName: ",mainName,'    mainPath: ',mainPath);

    if (path !== currentPath) {
      console.log('fuxing: sub-route-change ',path);
      router.push({ path: path });
    }
  });

</script>

<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
</style>

my-base/src/views/danalyse/index.vue

<template>{{name}} --- {{ url }}
  <WujieVue width="100%" height="100%" :name="name" :url="url"/>
</template>

<script setup lang="ts">
// import { useRouter } from 'vue-router'
import { ref } from 'vue'
import WujieVue from 'wujie-vue3'
import { APPLICATION_NAME, getApplicationHost } from '../../wujieConfig/constants'

// const router = useRouter()
const name = APPLICATION_NAME.ANALYSIS
const url = ref(getApplicationHost(APPLICATION_NAME.ANALYSIS))


</script>

<style scoped ></style>

子应用

my-analysis/src/main.js

import { createApp } from "vue";
import { createPinia } from "pinia";

import App from "./App.vue";
import router from "./router";

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";

const app = createApp(App);

app.use(createPinia());
app.use(router);
app.use(ElementPlus);
// app.mount('#danalyse-vite-vue3')

if (window.__POWERED_BY_WUJIE__) {
  window.__WUJIE_MOUNT = () => {
    app.mount("#danalyse-vite-vue3");
  };
  window.__WUJIE_UNMOUNT = () => {
    app.unmount();
  };
} else {
  app.mount("#danalyse-vite-vue3");
}

my-analysis/src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
// import EmptyLayout from '../views/emptyLayout.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
        
    {
      path: '/', 
      redirect: '/danalyse/event',
    },
    {
      path: '/danalyse/event', 
      name: 'event',
      component: () => import('../views/danalyse/event.vue'),
    }, 
    {
      path: '/danalyse/session', 
      name: 'session',
      component: () => import('../views/danalyse/session.vue'),
    }, 
  ],
})

export default router

my-analysis/src/App.vue

<template>
  {{ route.path }}
  <!-- <div>
    <router-link to="/event">事件</router-link> |
    <router-link to="/session">session</router-link>
  </div> -->
  <el-button @click="handleClick">点击push event</el-button>
  <el-button @click="handleClick2">点击push session</el-button>
  <hr></hr>
  <RouterView />
</template>

<script setup>
import { watch,onMounted } from "vue";
import { useRoute, useRouter, RouterView } from "vue-router";
const route = useRoute();
const router = useRouter();

const handleClick = () => {
  router.push('/danalyse/event');
}

const handleClick2 = () => {
  router.push('/danalyse/session');
}

// fuxing: 子应用修改切换子路由时,通知父应用同步路由
watch(
  () => route.path,
  () => {
    console.log("fuxing route.path", route.path, window?.$wujie);
    window?.$wujie?.bus?.$emit("sub-route-change", "danalyse", route.path);
  }
);

// 父应用变化了,修改子应用路由
window.$wujie.bus.$on("baseRouteChange", (location) => {
  const { fullPath } = location;
  console.log('fuxing: baseRouteChange  xxx   ' , fullPath, ' ----- ', route.fullPath);
  
  if (route.fullPath !== fullPath) {
    // 分析
    if (fullPath.startsWith("/danalyse")) {
      router.replace(fullPath);
    }
  }
});

onMounted(() => {
  console.log('fuxing: analysis onMounted ' , window.BUS,window.request);
  window.request().then(res => {
    console.log('fuxing: analysis onMounted res ' , res);
  })
})
</script>

<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}
</style>


<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
    <style>
      body{
        /* 解决、el-select 位置偏移以及 el-table tootip 位置偏移的问题  el中依赖了poper.js 的fixed定位偏移,子应用的dom挂载在shadowRoot上,导致计算错误*/
        position: relative; 
      }
    </style>
  </head>
  <body>
    <div id="danalyse-vite-vue3"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>