记录微前端项目

项目概述

项目结构

base-pdp 是一个基于微前端架构的研发管理平台,采用 Vue 3 和 Pinia 构建主应用,通过 Wujie 微前端框架集成多个子系统。项目结构清晰,遵循功能模块化组织原则。

主应用位于 src 目录下,包含以下核心模块:

  • api:API 接口定义
  • components:通用与业务组件
  • config:全局配置(路由、权限、API 模块等)
  • layouts:布局组件,包括微前端容器
  • router:路由配置
  • stores:Pinia 状态管理
  • utils:工具函数
  • views:视图组件

子系统通过 layouts 中的 WujiePMP.vue 和 WujieMP.vue 组件集成,分别对应“产品管理平台”和“测试管理”子应用。

核心组件

主应用的核心组件包括:

  • main.ts:应用入口,负责初始化 Vue 实例、注册插件、配置微前端。
  • WujiePMP/WujieMP.vue:微前端容器组件,负责加载和渲染子应用。
  • user.ts:用户状态管理,存储 token、用户信息、权限等。
  • router.config.ts:路由配置,定义主应用与子应用的路由映射。

这些组件协同工作,实现主应用的全局状态管理、路由控制和子应用集成。

架构概览

base-pdp 采用微前端架构,主应用作为壳系统,负责用户认证、全局布局、菜单导航和状态共享。子应用(PMP、MP)作为独立系统运行在 iframe 中,通过 Wujie 框架实现沙箱隔离和通信。

主应用通过 setupApp 方法注册子应用,配置其 URL、生命周期钩子、通信 props 和插件。子应用通过 bus 事件与主应用通信,如路由变更、登出等。

详细组件分析

主应用启动流程分析
主应用的启动流程在 main.ts 中定义,是整个系统运行的起点。

启动流程图

Snipaste_2025-08-29_15-13-18.png
Snipaste_2025-08-29_15-13-36.png

依赖注入与插件注册
主应用通过 app.use() 方法注册多个插件和功能模块:

  • Pinia:状态管理,用于存储用户信息、权限、菜单等全局状态。
  • Vue Router:路由管理,控制页面导航。
  • WujieVue:微前端核心框架,提供子应用加载、沙箱、通信能力。
  • infiniteScroll:第三方无限滚动插件。
  • useAll:自定义全局配置注册函数,可能包含组件、指令等。

代码示例:

app.use(createPinia());
app.use(router);
app.use(WujieVue);
app.use(infiniteScroll);
useAll(app);

这些插件的注册顺序至关重要,例如 Pinia 必须在权限控制模块之前初始化,因为后者依赖前者。

微前端集成机制分析

主应用通过 setupApp 函数配置和注册子应用,实现微前端集成。

PMP 与 MP 子系统集成
主应用在 main.ts 中为 pmp 和 mp 两个子系统调用 setupApp:

setupApp({
    name: 'pmp',
    url: hostMap('pmp'),
    props,
    attrs,
    plugins,
    ...lifecycles,
});

setupApp({
    name: 'mp',
    url: hostMap('mp'),
    props,
    attrs,
    plugins,
    ...lifecycles,
});
  • name: 子应用唯一标识。
  • url: 子应用地址,由 hostMap 函数根据环境动态生成。
  • props: 传递给子应用的数据,如 jump 路由跳转函数、token 认证令牌。
  • plugins: 注入的插件,用于解决样式、SVG 显示等问题。
  • lifecycles: 生命周期钩子,用于监控子应用状态。

子应用通信机制
主应用通过 Wujie 的 bus 事件总线与子应用通信:

  • sub-route-change: 子应用路由变化时,通知主应用同步高亮菜单。
  • sub-route-back: 子应用触发浏览器后退。
  • sub-logout: 子应用登出,主应用刷新页面。

代码示例:

bus.$on('sub-route-change', (name, $route) => {
    const mainPath = `/${name}${$route.path}`;
    router.push({ path: mainPath, query: $route.query });
});

全局布局与路由控制

微前端容器组件
WujiePMP.vue 和 WujieMP.vue 是主应用与子应用之间的桥梁。

  • WujiePMP.vue:使用 <script setup> 语法,简洁地传递 props,如 userInfo、menuList、authInfo 等,实现状态共享。
  • WujieMP.vue:使用选项式 API,包含更复杂的错误处理逻辑,能捕获子应用的 unhandledrejection 和 error 事件,并提供“重新加载”按钮。

两者都通过计算属性 subUrl 动态生成子应用的 URL,将主应用路由路径映射到子应用内部路由。

路由配置
router.config.ts 定义了主应用的路由表。

  • constantRouterMap: 常量路由,如登录、404 页面。
  • asyncRouterMap: 动态路由,包含主应用视图和子应用入口。

子应用路由通过 component: () => import('@/layouts/WujiePMP.vue') 指向微前端容器,并设置 name: 'pmp-sub',其子路由的 path 以 /pmp/ 开头,component 为 BlankRawLayout,表示内容由子应用自身渲染。


Snipaste_2025-08-29_15-54-28.png

状态共享与通信机制

主应用通过两种方式与子应用共享状态:

  1. 通过 props 传递:在 setupApp 和微前端容器组件中,将用户信息、权限、菜单等作为 props 传递给子应用。子应用通过 window.$wujie.props 访问这些数据。
  2. 通过 bus 事件通信:主应用和子应用通过事件总线进行双向通信,如路由同步、登出通知等。、

此外,主应用使用 Pinia (user.ts) 集中管理用户状态。useUserStore 提供了 Login、GetUserInfo、Logout 等方法,并通过 bus.$emit('wujie-setToken') 通知子应用 token 变更。

依赖分析

主应用的核心依赖包括:

  • Vue 3: 前端框架。
  • Pinia: 状态管理。
  • Wujie-vue3: 微前端框架。
  • ant-design-vue: UI 组件库。
  • vite: 构建工具。

子应用通过 hostMap.ts 动态获取其部署地址,支持开发、测试、生产等多环境配置。plugin.ts 中的插件解决了微前端常见的样式隔离和资源加载问题。


Snipaste_2025-08-29_15-57-22.png

微前端架构

微前端架构原理

项目结构分析

项目采用基于Vue 3的微前端架构,主应用(base-pdp)通过Wujie框架集成多个远程子应用。项目结构清晰,按功能模块划分目录,核心微前端相关组件位于src/layouts目录下。

主应用负责全局状态管理、路由控制和用户认证,子应用包括PMP、MP、CMDB等独立系统,通过iframe沙箱方式嵌入主应用。这种设计实现了各子应用的技术栈独立和团队解耦。


Snipaste_2025-08-29_16-00-27.png

核心架构概述

base-pdp项目采用Wujie微前端框架实现多应用集成,核心架构基于iframe沙箱技术,通过JS沙箱、样式隔离和资源预加载等机制,确保子应用间的独立性和安全性。

主应用作为壳系统,提供统一的登录认证、菜单导航和布局框架。子应用以独立部署的方式运行,通过配置化的URL映射动态加载。这种架构支持团队独立开发、测试和部署,有效降低了大型项目的协作成本。


Snipaste_2025-08-29_16-01-16.png
Snipaste_2025-08-29_16-01-27.png

主应用初始化流程

主应用的初始化逻辑在main.ts文件中实现,通过Vue 3的createApp API创建应用实例,并集成Wujie微前端框架。

import WujieVue from 'wujie-vue3';
const { bus, setupApp } = WujieVue;
import lifecycles from './lifecycles';
import hostMap from './hostMap';

const app = createApp(App);
app.use(createPinia());
app.use(router);
app.use(WujieVue);

// 注册子应用
setupApp({
    name: 'pmp',
    url: hostMap('pmp'),
    exec: true,
    props: { jump, token },
    degrade,
    ...lifecycles,
});

setupApp({
    name: 'mp',
    url: hostMap('mp'),
    exec: true,
    props: { jump, token },
    degrade,
    ...lifecycles,
});

初始化过程包含以下关键步骤:

  1. 创建Vue应用实例并挂载核心插件(Pinia、Router)
  2. 注册WujieVue插件,获取事件总线(bus)和应用注册方法(setupApp)
  3. 通过hostMap工具函数获取子应用的运行时URL
  4. 调用setupApp注册PMP和MP子应用,配置通信参数和生命周期钩子

子应用隔离机制

Wujie框架通过多重机制实现子应用间的完全隔离,确保各应用互不干扰。

JS沙箱机制
Wujie通过Proxy代理技术创建JS沙箱环境,拦截子应用对全局对象(window、document等)的访问和修改。每个子应用拥有独立的执行上下文,变量和函数不会污染主应用或其他子应用。

样式隔离机制
采用CSS作用域控制实现样式隔离:

  • 子应用样式被限制在iframe内部
  • 通过Shadow DOM或样式前缀避免全局样式冲突
  • 插件机制解决特殊样式问题(如SVG图标显示)

资源隔离
每个子应用的静态资源(JS、CSS、图片)独立加载,通过iframe的同源策略实现天然隔离。主应用不共享任何资源给子应用,确保技术栈完全独立。

Snipaste_2025-08-29_16-04-13.png

生命周期管理

微前端架构通过精细化的生命周期管理控制子应用的加载、渲染和销毁过程。生命周期钩子在lifecycles.ts文件中定义,并在主应用初始化时注入。

const lifecycles = {
    beforeLoad: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 加载前`),
    beforeMount: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 挂载前`),
    afterMount: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 挂载后`),
    beforeUnmount: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 卸载前`),
    afterUnmount: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 卸载后`),
    activated: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 激活`),
    deactivated: (appWindow) => console.log(`[wujie-${appWindow.__WUJIE?.id}] 停用`),
    loadError: (url, e) => {
        console.log(`[wujie] ${url} 加载失败`);
        console.error(e);
    },
};

生命周期流程如下:

  1. beforeLoad: 子应用资源开始加载前触发
  2. beforeMount: 子应用DOM挂载前触发
  3. afterMount: 子应用挂载完成后触发
  4. activated/deactivated: 子应用激活/停用时触发(用于保活模式)
  5. beforeUnmount/afterUnmount: 子应用卸载前后触发

此外,提供了refreshApp方法用于主动清除子应用沙箱缓存,实现应用刷新功能。

通信机制设计

主应用与子应用之间通过事件总线(Event Bus)和Props传递实现双向通信。

事件总线通信
基于Wujie的bus对象实现跨应用事件通信:

// 主应用监听子应用路由变化
bus.$on('sub-route-change', (name, $route) => {
    const mainPath = `/${name}${$route.path}`;
    router.push({ path: mainPath, query: $route.query });
});

bus.$on('sub-route-back', () => {
    router.back();
});

bus.$on('sub-logout', () => {
    location.reload();
});

Props数据传递
主应用通过props向子应用传递必要数据:

// 传递跳转方法和认证信息
const props = {
    jump: (location) => {
        router.push(location);
    },
    token: lStorage.get('token'),
};

子应用异常处理
MP子应用实现了完善的异常捕获机制,监听子应用内的错误事件并反馈给主应用:

autoCatchError() {
    const subWindow = window.document.querySelector('iframe[name=mp]')?.contentWindow;
    
    // 捕获未处理的Promise拒绝
    subWindow.addEventListener('unhandledrejection', onUnhandledRejection);
    // 捕获运行时错误
    subWindow.addEventListener('error', onError);
}

资源预加载与性能优化

项目通过资源预加载策略提升子应用的加载性能,减少用户等待时间。

预加载配置
在main.ts中通过setupApp的exec参数控制预加载行为:

setupApp({
    name: 'pmp',
    url: hostMap('pmp'),
    exec: true, // true表示预执行子应用代码
    // ...
});

当exec设置为true时,子应用资源加载后立即执行代码,用户访问时可快速呈现。

动态URL映射
通过hostMap.ts实现环境自适应的URL映射:

export default function hostMap(app: string) {
    if (import.meta.env.MODE === 'production') {
        return getPrefixUrl(app); // 生产环境使用端口映射
    }
    return map[app].devUrl; // 开发环境使用配置的开发URL
}

按需加载
子应用布局组件根据路由动态计算加载URL,实现按需加载:

<template>
    <WujieVue :url="subUrl" />
</template>

<script setup>
const subUrl = computed(() => {
    const path = route.fullPath.replace('/pmp/', '');
    return hostMap('pmp') + `${path}`;
});
</script>

插件扩展机制

Wujie框架通过插件机制提供灵活的扩展能力,plugin.ts文件定义了多个插件来解决特定问题。

const plugins = [
    {
        htmlLoader: (code) => code,
        jsLoader: (code) => code,
        cssLoader: (code) => code,
    },
    // 解决SVG图标不显示问题
    {
        appendOrInsertElementHook(element, iframeWindow) {
            if (element.nodeName === 'svg' && /* 条件判断 */) {
                iframeWindow.__WUJIE.styleSheetElements.push(element);
            }
        },
    },
    // 解决Vite5+Vue3样式元素被移除问题
    {
        patchElementHook(element, iframeWindow) {
            if (element.nodeName === 'STYLE') {
                element.insertAdjacentElement = function (_position, ele) {
                    iframeWindow.document.head.appendChild(ele);
                };
            }
        },
    },
];

插件类型包括:

  • 加载器插件:拦截HTML、JS、CSS资源的加载过程
  • DOM操作钩子:在元素添加或插入时执行自定义逻辑
  • 元素补丁钩子:修改元素的默认行为

这些插件解决了微前端场景下的常见兼容性问题,增强了框架的适应能力。

架构优势总结

基于Wujie框架的微前端架构为base-pdp项目带来了显著的技术优势:

团队独立开发
各子应用团队可以独立选择技术栈(Vue、React等),独立开发、测试和部署,大幅提升开发效率和团队协作灵活性。

技术栈异构
主应用与子应用可以使用不同的前端框架和技术版本,避免了技术栈升级的全局影响,支持渐进式技术演进。

独立部署
子应用可以独立发布,无需与主应用同步部署,降低了发布风险和协调成本,支持持续交付和灰度发布。

故障隔离
子应用间的完全隔离确保了单个应用的故障不会影响整个系统,提高了整体系统的稳定性和可靠性。

渐进式迁移
支持将传统单体应用逐步拆分为微前端架构,降低架构升级的风险和成本。

该架构特别适合大型企业级应用,能够有效管理复杂度,支持多团队并行开发,是现代前端架构的优秀实践。

子应用集成机制

项目结构

项目采用基于功能的文件组织方式,主要模块包括api、components、layouts、router、stores等。src/layouts目录下存放了主应用的布局组件,其中WujiePMP.vue和WujieMP.vue是用于集成PMP和MP子应用的关键布局组件。src/config目录下的router.config.ts文件定义了主应用的路由规则,而hostMap.ts文件则配置了子应用的入口地址。


Snipaste_2025-08-29_16-17-30.png

核心组件

核心组件包括WujiePMP.vue和WujieMP.vue两个布局组件,它们通过WujieVue组件实现对远程子应用的挂载和管理。WujieVue是无界微前端框架提供的Vue3组件,用于在主应用中安全地加载和运行远程子应用。

架构概述

主应用通过无界微前端框架实现对PMP和MP子应用的集成。当用户访问以/pmp或/mp开头的URL时,主应用的路由会匹配到对应的布局组件WujiePMP.vue或WujieMP.vue。这些布局组件内部使用WujieVue组件,根据当前路由动态构建子应用的入口URL,并将其挂载到指定的DOM节点中。主应用与子应用之间通过props传递数据,如用户信息、权限信息等,并通过事件总线进行通信。


Snipaste_2025-08-29_16-18-41.png

详细组件分析

WujiePMP.vue 分析
WujiePMP.vue是PMP子应用的布局组件,其主要功能是通过WujieVue组件挂载和管理PMP子应用。

代码结构

<template>
    <div class="pmp-sub">
        <WujieVue
            width="100%"
            height="100%"
            name="pmp"
            :alive="false"
            :sync="false"
            :url="subUrl"
            :props="{ jump, token, firstRedirectPath, userInfo, menuList, dicOptions, authInfo }"
        />
    </div>
</template>

属性配置

  • name: 子应用的唯一标识符,用于区分不同的子应用实例。
  • alive: 设置为false,表示不启用保活模式,每次URL变化都会重新渲染子应用实例。
  • sync: 设置为false,表示不将子应用的路由状态同步到主应用。
  • url: 通过计算属性subUrl动态生成子应用的入口URL。
  • props: 向子应用传递数据,包括跳转函数jump、用户令牌token、用户信息userInfo、菜单列表menuList、字典选项dicOptions和权限信息authInfo。

数据传递
WujiePMP.vue通过props向PMP子应用传递了丰富的上下文信息:

  • jump: 一个函数,允许子应用通过主应用的路由进行页面跳转。
  • token: 用户的认证令牌,用于子应用的身份验证。
  • userInfo: 当前登录用户的基本信息。
  • menuList: 用户的菜单权限列表。
  • dicOptions: 字典数据,用于下拉框等组件的数据源。
  • authInfo: 用户的详细权限信息。

WujieMP.vue 分析
WujieMP.vue是MP子应用的布局组件,其功能与WujiePMP.vue类似,但包含了更复杂的错误处理和生命周期管理。

代码结构

<template>
    <div class="mp-sub">
        <div v-if="!isError" style="width: 100%; height: 100%">
            <WujieVue
                width="100%"
                height="100%"
                name="mp"
                v-bind="lifecycles"
                :alive="false"
                :sync="false"
                :url="subUrl"
                :props="{ jump, token }"
                :loadError="handleMpLoadError"
                :activated="handleActivated"
                :deactivated="handleDeactivated"
                :beforeMount="handleBeforeMount"
                :beforeUnmount="handleBeforeUnMount"
            ></WujieVue>
        </div>
        <div v-else class="mp-sub__error">
            <a-result status="500" title="客户端异常,请稍后再试!" sub-title="Error">
                <template #extra>
                    <a-button type="primary" @click="handleRefresh">重新加载</a-button>
                </template>
            </a-result>
        </div>
    </div>
</template>

错误处理
WujieMP.vue实现了完善的错误处理机制:

isError: 一个布尔值,用于控制是否显示错误提示页面。
handleMpLoadError: 当子应用加载失败时触发,将isError设置为true,显示错误页面。
handleRefresh: 用户点击“重新加载”按钮时调用,刷新整个页面以尝试恢复。
生命周期钩子
WujieMP.vue通过v-bind="lifecycles"将lifecycles.ts中定义的生命周期钩子绑定到WujieVue组件上,并额外定义了handleActivated、handleDeactivated等方法,用于在子应用激活和失活时执行特定逻辑,如自动捕获子应用的异常。

异常捕获
WujieMP.vue通过autoCatchError方法实现了对子应用异常的主动捕获:

  • 监听子应用iframe的unhandledrejection事件,捕获未处理的Promise拒绝。
  • 监听子应用iframe的error事件,捕获JavaScript运行时错误。
  • 通过useWujieBusOn监听子应用通过事件总线发送的TMP_ERROR异常事件。

路由配置分析
主应用的路由配置在router.config.ts文件中定义,通过asyncRouterMap数组配置了PMP和MP子应用的路由规则。

PMP子应用路由

{
    path: '/pmp',
    name: 'pmp-sub',
    meta: { title: '产品管理平台', hide: true },
    component: () => import('@/layouts/WujiePMP.vue'),
    children: [
        {
            path: '/pmp/workBoard/versionBoard',
            name: 'versionBoard',
            component: BlankRawLayout,
            meta: { title: '版本看板' },
        },
        // ... 其他子路由
    ],
}
  • path: /pmp,所有PMP子应用的URL都以此为前缀。
  • component: 指向WujiePMP.vue布局组件。
  • children: 定义了PMP子应用内部的路由映射,如/pmp/workBoard/versionBoard映射到“版本看板”。

MP子应用路由

{
    path: '/mp',
    name: 'mp-sub',
    meta: { title: '测试管理', hide: true },
    component: () => import('@/layouts/WujieMP.vue'),
    children: [
        {
            path: '/mp/product-risk/list',
            name: 'productRiskList',
            component: BlankRawLayout,
            meta: { title: '产品风险' },
        },
        // ... 其他子路由
    ],
}
  • path: /mp,所有MP子应用的URL都以此为前缀。
  • component: 指向WujieMP.vue布局组件。
  • children: 定义了MP子应用内部的路由映射。

URL映射机制
当用户访问/pmp/workBoard/versionBoard时,主应用的Vue Router会匹配到WujiePMP.vue组件。在WujiePMP.vue中,subUrl计算属性会执行以下操作:

  1. 获取当前完整路径:route.fullPath(例如/pmp/workBoard/versionBoard)。
  2. 移除/pmp/前缀,得到子应用内部路径:workBoard/versionBoard。
  3. 调用hostMap('pmp')获取PMP子应用的基地址(例如//localhost:8082/)。
  4. 拼接基地址和内部路径,得到最终的子应用入口URL://localhost:8082/workBoard/versionBoard。

依赖分析

主应用通过package.json依赖了wujie-vue3库,版本为^1.0.28。main.ts文件中通过app.use(WujieVue)注册了无界插件,使其在全局可用。WujiePMP.vue和WujieMP.vue组件直接依赖wujie-vue3库中的WujieVue组件来实现子应用的挂载。


Snipaste_2025-08-29_17-35-22.png

性能考虑

  • 按需加载: 子应用的布局组件(如WujiePMP.vue)是通过import()动态导入的,实现了路由级别的代码分割和懒加载。
  • 资源隔离: 无界框架通过iframe或Web Worker技术实现了子应用的资源隔离,避免了样式和脚本的全局污染。
  • 内存管理: WujiePMP.vue和WujieMP.vue都设置了alive="false",这意味着子应用在切换时会被销毁,有助于释放内存,但会牺牲一定的切换速度。

故障排除指南

  • 子应用加载失败: 检查hostMap.ts中配置的子应用URL是否正确,确保子应用服务已启动。
  • 样式错乱: 确认主应用和子应用的CSS作用域是否正确隔离,避免全局样式冲突。
  • 通信失败: 检查事件总线的事件名称是否拼写正确,确保主应用和子应用都正确注册了事件监听器。
  • 数据传递失败: 确认props传递的数据类型和结构是否符合子应用的预期。

应用间通信机制

核心通信机制

通信架构概述
系统采用事件总线(Event Bus)模式实现应用间通信,主应用与各子应用通过共享的全局事件总线进行消息的发布(emit)与订阅(on)。该模式实现了组件间的松耦合,使得主应用可以通知子应用执行操作,子应用也可以回调通知主应用更新状态。

Snipaste_2025-08-29_17-38-06.png

事件总线封装分析

Wujie 事件总线封装
useWujie.ts 文件对 WujieVue 的事件总线进行了封装,提供了更安全、类型更明确的 API。
事件常量定义
文件中定义了 WujieBusEvents 常量对象,集中管理所有事件名称,避免硬编码,提高可维护性。

export const WujieBusEvents = {
    /** 测试管理平台 抽屉事件 */
    TMP_DRAWER: 'tmp-drawer',
    /** 测试管理平台 抽屉事件 */
    TMP_DRAWER_EVENT: 'tmp-drawer-emits',
    /** 测试管理平台 关闭所有抽屉事件 */
    TMP_DRAWER_CLOSE_ALL: 'tmp-drawer-close-all',
    /** 产品管理平台抽屉 */
    PMP_DRAWER: 'pmp-drawer',
    // ... 其他事件
} as const;

类型安全
通过 as const 和类型推断,确保事件名称的类型安全。

type WujieBusEventsType = (typeof WujieBusEvents)[keyof typeof WujieBusEvents];

事件操作封装
提供了 useWujieBusOn、useWujieBusEmit 和 useWujieBusOff 三个函数,分别用于监听、触发和移除事件监听。

Snipaste_2025-08-29_17-40-01.png

跨子应用通信实例

主应用向子应用传递参数
在 TmpDrawers.vue 中,主应用通过 ref 直接调用子应用的方法,并通过 props 传递配置参数。

<template>
    <div>
        <TestCaseInfoDrawer
            ref="TestCaseInfoDrawer"
            @submit="e => handleDrawerEventListener('TestCase', 'submit', e)"
        />
        <!-- 其他抽屉组件 -->
    </div>
</template>

<script>
export default {
    computed: {
        TestCaseDrawerRef() {
            return this.$refs.TestCaseInfoDrawer;
        },
    },
    methods: {
        handleDrawerEventListener(type, eventName, payload) {
            emitTmpDrawer({ type, data: payload, event: eventName });
        },
    },
};
</script>

子应用回调通知主应用
子应用通过 $emit 触发事件,主应用在 TmpDrawers.vue 中监听并处理。

Snipaste_2025-08-29_17-41-07.png

跨子应用通信:转需求
ProductFaultInfoDrawer.vue 中的 handleCallPmpRequire 方法展示了如何通过事件总线调用另一个子应用(PMP)的功能。

handleCallPmpRequire() {
    this.closeDrawer();
    useWujieBusEmit(WujieBusEvents.PMP_DRAWER, {
        component: 'CreatePro',
        componentProps: {
            sourceInfo: {
                requireName: `【缺陷推送】${faultName}`,
                // ... 其他预填数据
            },
            sourceByFault: id,
        },
        callback: () => {
            // 回调逻辑
        },
    });
}

通信规范与最佳实践

事件命名规范

  • 采用小写字母和连字符(kebab-case)命名,如 tmp-drawer。
  • 按功能模块前缀划分,如 TMP_ 表示测试管理平台,PMP_ 表示产品管理平台。
  • 语义清晰,避免歧义。

通信时序控制

  • 事件监听时机:在组件 mounted 钩子中绑定监听,在 beforeUnmount 中移除。
  • 事件触发时机:在操作完成且数据稳定后触发,避免频繁触发。
  • 异步处理:对于耗时操作,使用 async/await 确保时序正确。

错误处理与内存泄漏防护

错误处理机制

  • 事件监听检查:在触发事件前,检查目标组件是否存在。
  • 异常捕获:在事件处理函数中使用 try-catch 捕获潜在错误。
  • 日志记录:关键操作添加 console.log 便于调试。
    防止内存泄漏
  • 及时解绑:在组件销毁时,必须调用 useWujieBusOff 移除事件监听。
  • 清理引用:在 beforeUnmount 钩子中清理 ref 和定时器。

路由同步策略

项目结构

项目采用基于功能的模块化结构,清晰地分离了主应用与多个子应用(PMP、MP)。主应用负责全局路由、布局和状态管理,而子应用作为独立的模块通过iframe嵌入。路由配置、布局组件和微前端集成点是理解整个系统的关键。


Snipaste_2025-08-29_17-43-34.png

核心组件

系统的核心组件包括主应用的路由配置、Wujie布局组件、主应用入口文件和事件总线。这些组件协同工作,实现了微前端环境下的路由同步。

架构概览

该微前端系统采用主从架构。主应用作为容器,负责管理全局状态、用户认证和路由分发。子应用(PMP和MP)是独立开发、独立部署的前端应用,通过Wujie框架以iframe的形式嵌入主应用。主应用通过路由前缀(如/pmp/和/mp/)来识别和分发请求,并利用Wujie的事件总线实现主子应用间的双向通信。

Snipaste_2025-08-29_17-44-15.png

详细组件分析

主应用路由配置分析
主应用的router.config.ts文件定义了静态路由和动态路由。asyncRouterMap中的/pmp和/mp路径是关键的微前端接入点。

Snipaste_2025-08-29_17-44-59.png

Wujie布局组件分析
WujiePMP.vue和WujieMP.vue是子应用的容器组件。它们使用WujieVue组件来加载子应用。
MP子应用加载机制
MP子应用的布局组件WujieMP.vue除了加载子应用外,还集成了更复杂的错误处理和生命周期监听。

Snipaste_2025-08-29_17-45-48.png

主应用入口与事件总线分析
main.ts是应用的入口,负责初始化Wujie框架和设置事件监听。

Snipaste_2025-08-29_17-46-17.png

依赖分析

系统依赖关系清晰,主应用是核心,所有子应用和工具都依赖于它。


Snipaste_2025-08-29_17-46-58.png

性能考虑

  • 预加载: 代码中注释掉了preloadApp,说明团队考虑过预加载以提升性能,但可能因“防止子应用提前渲染路由”而禁用。
  • 沙箱缓存: lifecycles.ts中的refreshApp函数允许主动清除子应用的沙箱缓存,可用于解决内存泄漏或状态污染问题。
  • 网络请求: hostMap.ts根据环境(开发、生产)动态生成子应用URL,优化了网络请求路径。

故障排除指南

当出现路由同步问题或白屏时,可按以下步骤排查:

  1. 检查子应用URL: 确认hostMap.ts中配置的子应用URL是否正确,特别是端口号。
  2. 检查路由前缀: 确保主应用路由配置中/pmp和/mp的路径定义正确,且子应用内部路由与之匹配。
  3. 检查事件总线: 在浏览器控制台中,检查sub-route-change和routeChange事件是否被正确触发和监听。
  4. 检查iframe加载: 查看WujiePMP.vue和WujieMP.vue中的subUrl计算结果是否正确,并确认iframe是否成功加载子应用。
  5. 处理加载错误: WujieMP.vue中实现了对ChunkLoadError和SyntaxError的捕获,若子应用加载失败,会显示错误页面并提供“重新加载”按钮。
  6. 清除缓存: 使用refreshApp函数或强制刷新页面来清除可能的沙箱缓存问题。

结论

主应用通过router.config.ts分配专属路由前缀,并利用Wujie布局组件进行路由劫持。子应用的URL通过移除前缀并拼接主域名来构建。主应用通过bus.$on('sub-route-change')监听子应用的路由变化,确保浏览器地址栏与当前视图同步。同时,子应用通过useWujieBusEmit(WujieBusEvents.ROUTE_CHANGE)将内部路由变化通知给主应用,形成闭环。这种基于事件总线的双向通信模式,有效地解耦了主子应用,实现了无缝的用户体验。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容