微前端 出现的历史背景?
微前端的诞生,源于现代前端开发在应对大型、复杂、长周期项目时,传统单体前端架构暴露出的一系列系统性痛点。
- 代码冲突频繁:多个团队在同一个 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
bash
# 创建基座项目
yarn create vite
# 安装micro-app
yarn add -D @micro-zoe/micro-app
在入口文件修改配置 microapp-base/src/main.tsx
初始化 micro-app
js
import microapp from '@micro-zoe/micro-app'
microapp.start()
添加路由 microapp-base/src/App.tsx
js
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 (
<>
<BrowserRouter>
<Routes>
<Route
path="/"
element={
<div>
<p>Click me times: {count}</p>
<Link to="/app1">Click me app 1</Link>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click me times
</button>
<Link to="/app2">Click me app2 </Link>
<Outlet />
</div>
}
>
<Route path="/app1/*" element={<MyPage />} />
<Route path="/app2/*" element={<MyPageVue />} />
</Route>
</Routes>
</BrowserRouter>
</>
);
}
export default App;
子应用1(React19 + vite8 )
micro-app通过自定义元素<micro-app>加载子应用
js
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 (
<div>
<micro-app
name="app1"
iframe
// 这里的路径后面有 /react19-vite-app,是因为子应用 路由配置了 basename
url="http://localhost:5177/react19-vite-app/"
data={{
user: {
name: "lili",
},
}}
/>
</div>
);
}
export default MyPage;
子应用2 (vue3 + vite8)
js
import React from "react";
export function MyPage() {
const ele = React.createElement("micro-app", {
name: "app1",
iframe: true,
url: "http://localhost:5174/yo/",
});
return <div>{ele}</div>;
}
export default MyPage;
子应用1 (React19 + vite8 )
js
yarn add -D @micro-zoe/micro-app
vite.config.ts 文件配置
micro-app 从主应用通过 fetch 加载子应用的静态资源,由于主应用与子应用的域名不一定相同,所以子应用需要支持跨域。
js
server: {
port: "5177",
header: {
"Access-Control-Allow-Origin: "*"
}
}
初始化
react19-vite-app/src/main.tsx
js
import microApp, { EventCenterForMicroApp } from "@micro-zoe/micro-app";
declare global {
interface Window {
microApp?: EventCenterForMicroApp;
}
}
// 启动 micro-app
microApp.start();

子应用1 路由配置了 basename

子应用2 (vue3 + vite6)
js
npm i -D @micro-zoe/micro-app --save-dev
vite.config.ts文件配置
js
server: {
port: "5174",
header: {
"Access-Control-Allow-Origin: "*"
}
}
主应用与子应用间的通信
示例 主应用发送数据,子应用接收数据
主应用
js
import microApp from "@micro-zoe/micro-app";
<button
onClick={() => {
// 给app1发送数据数据
microApp.setData("app1", {
user: "xxxlili",
});
}}
>
Set data
</button>
子应用 app1
js
<Button
onClick={() => {
console.log('xxxwww', window?.microApp?.getData());
}}
>
测试
</Button>
js
import { EventCenterForMicroApp } from "@micro-zoe/micro-app";
declare global {
interface Window {
microApp?: EventCenterForMicroApp;
}
}
js
console.log("window.microApp", window?.microApp);

示例 子应用发送数据,主应用接收数据
子应用 app1
js
<Button
onClick={() => {
window?.microApp?.dispatch({
type: "test",
data: {
name: "team",
},
});
}}
>
发送数据
</Button>
主应用
js
import microApp from "@micro-zoe/micro-app";
<button
onClick={() => {
// 给app1发送数据数据
const data = microApp.getData("app1");
console.log("收到来自子应用app1的data", data);
}}
>
get data
</button>
样式隔离
micro-app 的样式隔离核心机制是 Scoped CSS ,即通过为子应用的每个样式规则 动态添加带有应用特定 name 属性的前缀 ,来将其作用域限制在对应的 <micro-app> 标签内。
工作原理 :当一个子应用被加载和渲染时,micro-app 会拦截其注入页面的所有 <style> 标签,并执行"CSS 规则重写"。系统会重写每条CSS规则的选择器,为它们加上一个前缀。

示例 指定子应用禁用样式隔离
设置 disableScopecss='
js
<micro-app
name="app1"
iframe
disableScopecss
url="http://localhost:5177/react19-vite-app/"
data={{
user: {
name: "lili",
},
}}
/>
