微前端架构¶
📚 章节目标¶
本章节将全面介绍微前端架构的各种技术和方案,包括qiankun、single-spa、Module Federation等,帮助学习者掌握微前端架构的核心方法。
学习目标¶
- 理解微前端架构的核心概念
- 掌握qiankun微前端框架
- 掌握single-spa微前端
- 掌握Module Federation
- 理解微前端通信机制
- 掌握微前端最佳实践
🏗️ 微前端架构概述¶
1. 什么是微前端¶
JavaScript
// 微前端定义
// 将前端应用拆分成多个小型、独立的应用
// 每个应用可以独立开发、部署、运行
// 最终组合成一个完整的应用
// 微前端的优势
// 1. 独立开发 - 团队可以独立开发各自的应用
// 2. 独立部署 - 每个应用可以独立部署
// 3. 技术栈无关 - 可以使用不同的技术栈
// 4. 增量升级 - 可以逐步升级技术栈
// 5. 故障隔离 - 一个应用故障不会影响其他应用
// 微前端的挑战
// 1. 应用间通信 - 需要设计通信机制
// 2. 共享依赖 - 需要处理共享依赖
// 3. 样式隔离 - 需要处理样式冲突
// 4. 性能优化 - 需要优化加载性能
2. 微前端架构模式¶
JavaScript
// 微前端架构模式
// 1. 基于路由 - 根据路由加载不同应用
// 2. 基于组件 - 将应用作为组件加载
// 3. 基于iframe - 使用iframe隔离应用
// 4. 基于Web Components - 使用Web Components技术
// 基于路由的微前端
const routes = {
'/home': loadApp('home'),
'/dashboard': loadApp('dashboard'),
'/settings': loadApp('settings'),
};
// 基于组件的微前端
function App() {
return (
<div>
<HomeApp />
<DashboardApp />
<SettingsApp />
</div>
);
}
🎭 qiankun微前端框架¶
1. qiankun基础¶
1.1 主应用配置¶
JavaScript
// 安装qiankun
npm install qiankun
// 主应用入口
import { registerMicroApps, start } from 'qiankun';
// 注册微应用
registerMicroApps([
{
name: 'react-app',
entry: '//localhost:7100',
container: '#subapp-viewport',
activeRule: '/react',
},
{
name: 'vue-app',
entry: '//localhost:7200',
container: '#subapp-viewport',
activeRule: '/vue',
},
{
name: 'angular-app',
entry: '//localhost:7300',
container: '#subapp-viewport',
activeRule: '/angular',
},
]);
// 启动qiankun
start({
sandbox: {
// 二选一:strictStyleIsolation使用Shadow DOM,兼容性较差
// experimentalStyleIsolation使用样式前缀,兼容性更好
experimentalStyleIsolation: true,
},
});
1.2 子应用配置¶
JavaScript
// React子应用
// src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// src/index.js
import './public-path';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
let root = null;
function render(props = {}) {
const { container } = props;
const dom = container
? container.querySelector('#root')
: document.querySelector('#root');
root = ReactDOM.createRoot(dom);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('[react-app] bootstrap');
}
export async function mount(props) {
console.log('[react-app] mount', props);
render(props);
}
export async function unmount(props) {
console.log('[react-app] unmount', props);
root?.unmount();
root = null;
}
// Vue 3 子应用
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
let app = null;
function render(props = {}) {
const { container } = props;
app = createApp(App);
app.use(router);
const dom = container ? container.querySelector('#app') : '#app';
app.mount(dom);
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('[vue-app] bootstrap');
}
export async function mount(props) {
console.log('[vue-app] mount', props);
render(props);
}
export async function unmount(props) {
console.log('[vue-app] unmount', props);
app?.unmount();
app = null;
}
2. qiankun高级特性¶
2.1 应用间通信¶
JavaScript
// 主应用
import { initGlobalState } from 'qiankun';
// 初始化全局状态
const actions = initGlobalState({
user: null,
token: '',
});
// 监听状态变化
actions.onGlobalStateChange((state, prev) => {
console.log('State changed:', state, prev);
});
// 子应用(通过 mount 生命周期的 props 获取状态 API)
// React子应用
import { useState, useEffect, useCallback } from 'react';
let qiankunProps = null;
// 在 qiankun 生命周期中保存 props
export async function mount(props) {
qiankunProps = props;
render(props);
}
function App() {
const [globalState, setLocalState] = useState({ user: null, token: '' }); // 解构赋值:从对象/数组提取值
useEffect(() => {
// 通过 qiankun props 监听全局状态变化
qiankunProps?.onGlobalStateChange((state) => {
setLocalState(state);
}, true); // true = 立即触发一次回调
}, []);
const handleLogin = useCallback(() => {
qiankunProps?.setGlobalState({
user: { name: 'John' },
token: 'abc123',
});
}, []);
return (
<div>
<p>User: {globalState.user?.name}</p>
<button onClick={handleLogin}>Login</button>
</div>
);
}
// Vue 3 子应用
// 通过 mount 生命周期保存 props,再在组件中使用
import { ref } from 'vue';
let qiankunActions = null;
export async function mount(props) {
qiankunActions = props;
render(props);
}
// 在组件中使用
const globalState = ref({});
// setup 中初始化
qiankunActions?.onGlobalStateChange((state) => {
globalState.value = state;
}, true);
function updateGlobalState() {
qiankunActions?.setGlobalState({
user: { name: 'Jane' },
});
}
2.2 样式隔离¶
JavaScript
// qiankun样式隔离(两种方式二选一)
// 方式一:experimentalStyleIsolation(推荐,兼容性好,添加样式前缀)
start({
sandbox: {
experimentalStyleIsolation: true,
},
});
// 方式二:strictStyleIsolation(使用Shadow DOM,兼容性较差)
// start({
// sandbox: {
// strictStyleIsolation: true,
// },
// });
// 子应用样式
/* 子应用样式会自动添加前缀 */
.container {
background: #f0f0f0;
}
🎪 single-spa微前端¶
1. single-spa基础¶
1.1 主应用配置¶
JavaScript
// 安装single-spa
npm install single-spa
// 主应用入口
import { registerApplication, start } from 'single-spa';
// 注册微应用
registerApplication({
name: 'react-app',
app: () => System.import('react-app'),
activeWhen: (location) => location.pathname.startsWith('/react'),
});
registerApplication({
name: 'vue-app',
app: () => System.import('vue-app'),
activeWhen: (location) => location.pathname.startsWith('/vue'),
});
// 启动single-spa
start({
urlRerouteOnly: true,
});
2.2 子应用配置¶
JavaScript
// React子应用
// src/root.component.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import Root from './root.component';
let root = null;
export const bootstrap = async () => {
console.log('[react-app] bootstrap');
};
export const mount = async (props) => {
console.log('[react-app] mount', props);
const dom = document.getElementById('single-spa-application:react-app');
root = ReactDOM.createRoot(dom);
root.render(<Root />);
};
export const unmount = async (props) => {
console.log('[react-app] unmount', props);
root?.unmount(); // ?.可选链:对象为null/undefined时安全返回undefined
root = null;
};
// Vue 3 子应用
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
let app = null;
export const bootstrap = async () => {
console.log('[vue-app] bootstrap');
};
export const mount = async (props) => {
console.log('[vue-app] mount', props);
const dom = document.getElementById('single-spa-application:vue-app');
app = createApp(App);
app.use(router);
app.mount(dom);
};
export const unmount = async (props) => {
console.log('[vue-app] unmount', props);
app?.unmount();
app = null;
};
2. single-spa高级特性¶
2.1 应用间通信¶
JavaScript
// 使用single-spa的事件总线
// 主应用
import { navigateToUrl } from 'single-spa';
function handleNavigation(path) {
navigateToUrl(path);
}
// 子应用
// 发送事件
window.dispatchEvent(new CustomEvent('custom-event', {
detail: { data: 'Hello from React' },
}));
// 监听事件
window.addEventListener('custom-event', (event) => {
console.log('Received event:', event.detail);
});
📦 Module Federation¶
1. Module Federation基础¶
1.1 主应用配置¶
JavaScript
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
reactApp: 'reactApp@http://localhost:7100/remoteEntry.js',
vueApp: 'vueApp@http://localhost:7200/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: deps.react },
'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
vue: { singleton: true, requiredVersion: deps.vue },
},
}),
],
};
// 使用远程模块
import React, { lazy, Suspense } from 'react';
const RemoteReactApp = lazy(() => import('reactApp/App'));
const RemoteVueApp = lazy(() => import('vueApp/App'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<RemoteReactApp />
<RemoteVueApp />
</Suspense>
</div>
);
}
1.2 子应用配置¶
JavaScript
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin'); // const不可重新赋值;let块级作用域变量
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'reactApp',
filename: 'remoteEntry.js',
exposes: {
'./App': './src/App',
'./Button': './src/Button',
},
shared: {
react: { singleton: true, requiredVersion: deps.react },
'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
},
}),
],
};
2. Module Federation高级特性¶
2.1 动态加载远程模块¶
JavaScript
// 动态加载远程模块
async function loadRemoteModule() { // async定义异步函数;await等待Promise完成
const module = await import('reactApp/App'); // await等待异步操作完成
return module.default;
}
// 使用
const RemoteApp = React.lazy(() => loadRemoteModule()); // 箭头函数:简洁的函数语法
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<RemoteApp />
</Suspense>
);
}
📝 练习题¶
1. 基础题¶
题目1:配置qiankun主应用
JavaScript
// 配置qiankun主应用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
// 注册微应用
]);
start();
2. 进阶题¶
题目2:实现微前端应用间通信
JavaScript
// 实现微前端应用间通信
// 主应用
import { initGlobalState } from 'qiankun';
// 子应用
// React子应用
// Vue子应用
3. 面试题¶
题目3:解释微前端的优势和挑战
JavaScript
// 答案要点:
// 优势:
// 1. 独立开发 - 团队可以独立开发
// 2. 独立部署 - 每个应用可以独立部署
// 3. 技术栈无关 - 可以使用不同的技术栈
// 4. 增量升级 - 可以逐步升级技术栈
// 5. 故障隔离 - 一个应用故障不会影响其他应用
// 挑战:
// 1. 应用间通信 - 需要设计通信机制
// 2. 共享依赖 - 需要处理共享依赖
// 3. 样式隔离 - 需要处理样式冲突
// 4. 性能优化 - 需要优化加载性能
🎯 本章总结¶
本章节全面介绍了微前端架构的各种技术和方案,包括qiankun、single-spa、Module Federation等。关键要点:
- 微前端概念:理解微前端架构的优势和挑战
- qiankun:掌握qiankun微前端框架的配置和使用
- single-spa:掌握single-spa微前端的配置和使用
- Module Federation:掌握Module Federation的配置和使用
- 应用间通信:掌握微前端应用间通信机制
- 最佳实践:理解微前端最佳实践
下一步将深入学习服务端渲染技术。