微前端 Micro-App 实践
微前端 出现的历史背景?
微前端的诞生,源于现代前端开发在应对大型、复杂、长周期项目时,传统单体前端架构暴露出的一系列系统性痛点。
代码冲突频繁:多个团队在同一个 Git 仓库中修改代码,合并冲突家常便饭,解冲突消耗大量时间。
发布排队与相互阻塞:团队 A 的功能需要发布,但团队 B 的代码正在集成测试,导致发布窗口互相等待,无法独立上线。
沟通成本高:一次不小心改动可能影响其他团队的业务,导致需要全局回归测试,任何变更都需要跨团队沟通。
构建时间过长:每次开发启动、代码编译、热更新、生产打包都需要处理整个应用,耗时可能从几分钟到半小时,严重降低开发效率。
代码耦合严重:公用组件、工具函数在长期迭代中形成错综复杂的依赖,改一处可能影响多处,技术债务堆积,新人上手困难。
无法渐进式重构:想要升级底层框架(如从 Vue 2 到 Vue 3)或替换构建工具(Webpack 到 Vite),面对整个巨大应用,几乎不可能一次性完成。
微前端(Micro Frontends)
微前端(Micro Frontends)是一种前端架构风格,它将一个庞大、复杂的前端应用,按照业务边界或功能域,拆分成多个更小、独立开发、独立部署、独立运行的子应用。这些子应用可以由不同的团队使用相同或不同的技术栈构建,并通过一个“基座”应用(或称容器、主应用)将它们无缝地组合成一个统一的用户界面。
目前主流的微前端架构,大致可以分为运行时框架、构建时方案和基础技术三大流派。
运行时框架
qiankun,基于 single-spa 封装,采用沙箱与动态注入Micro-App,基于 Web Components 封装,以标签形式加载Wujie (无界),Web Components + iframe 增强,利用 iframe 实现 JS 沙箱Single-SPA,纯粹的路由级生命周期调度器
构建时方案
Module Federation,Webpack 5 插件,实现运行时模块共享
基础技术
iframe,浏览器原生机制,创建独立上下文Web Components,浏览器原生组件化方案
Micro-App 实现微前端
Micro-App基于 Web Components 封装,以标签形式加载子应用,原生支持 vite。
类 Web Component
自定义元素:将子应用的生命周期(加载、渲染、卸载)封装在
micro-app自定义元素中,用户只需像使用普通 HTML 标签一样声明即可。虚拟路由系统:
micro-app为每个子应用创建独立的路由系统,并与主应用的路由进行隔离。主应用可通过 API 控制子应用跳转,同时子应用的路由信息也会同步到浏览器地址栏,解决了多应用同屏时的路由冲突问题。HTML Entry 机制:基于 HTML 入口进行资源加载,而非 JS 入口。这种机制使子应用的接入成本极低,几乎不需要修改代码,并能保证子应用独立运行与在微前端中表现一致。
基座项目 React19 + vite8
node v26.0.0
# 创建基座项目
yarn create vite
# 安装micro-app
yarn add -D @micro-zoe/micro-app在入口文件修改配置 microapp-base/src/main.tsx
初始化 micro-app
import microapp from '@micro-zoe/micro-app'
microapp.start()添加路由 microapp-base/src/App.tsx
import { useState } from "react";
import "./App.css";
import MyPage from "./pages/SubReact.tsx";
import MyPageVue from "./pages/SubVue.tsx";
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";
function App() {
const [count, setCount] = useState(0);
return (
<>
Click me times: {count}
Click me app 1
Click me app2
子应用1(React19 + vite8 )
micro-app通过自定义元素加载子应用
import React, { useEffect } from "react";
import microApp from "@micro-zoe/micro-app";
export function MyPage() {
useEffect(() => {
microApp.setData("app1", {
user: {
name: "lili",
},
});
}, []);
// const ele = React.createElement("micro-app", {
// name: "app1",
// iframe: true,
// url: "http://localhost:5177/react19-vite-app/",
// data: {
// user: {
// name: "lili",
// },
// },
// });
return (
);
}
export default MyPage;子应用2 (vue3 + vite8)
import React from "react";
export function MyPage() {
const ele = React.createElement("micro-app", {
name: "app1",
iframe: true,
url: "http://localhost:5174/yo/",
});
return {ele};
}
export default MyPage;子应用1 (React19 + vite8 )
yarn add -D @micro-zoe/micro-appvite.config.ts 文件配置
micro-app 从主应用通过 fetch 加载子应用的静态资源,由于主应用与子应用的域名不一定相同,所以子应用需要支持跨域。
server: {
port: "5177",
header: {
"Access-Control-Allow-Origin: "*"
}
}初始化
react19-vite-app/src/main.tsx
import microApp, { EventCenterForMicroApp } from "@micro-zoe/micro-app";
declare global {
interface Window {
microApp?: EventCenterForMicroApp;
}
}
// 启动 micro-app
microApp.start();子应用1 路由配置了 basename
子应用2 (vue3 + vite6)
npm i -D @micro-zoe/micro-app --save-devvite.config.ts文件配置
server: {
port: "5174",
header: {
"Access-Control-Allow-Origin: "*"
}
}主应用与子应用间的通信
示例 主应用发送数据,子应用接收数据
主应用
import microApp from "@micro-zoe/micro-app";
子应用 app1
import { EventCenterForMicroApp } from "@micro-zoe/micro-app";
declare global {
interface Window {
microApp?: EventCenterForMicroApp;
}
}console.log("window.microApp", window?.microApp);示例 子应用发送数据,主应用接收数据
子应用 app1
主应用
import microApp from "@micro-zoe/micro-app";
样式隔离
micro-app 的样式隔离核心机制是 Scoped CSS,即通过为子应用的每个样式规则 动态添加带有应用特定 name 属性的前缀,来将其作用域限制在对应的 标签内。
工作原理:当一个子应用被加载和渲染时,micro-app 会拦截其注入页面的所有 标签,并执行“CSS 规则重写”。系统会重写每条CSS规则的选择器,为它们加上一个前缀。
示例 指定子应用禁用样式隔离
设置 disableScopecss='