前端微服务架构实践 🏗️
引言
随着前端应用规模的不断扩大,微服务架构在前端领域的应用越来越广泛。本文将深入探讨前端微服务架构的实现方案、最佳实践和相关工具。
微服务架构概述
前端微服务架构主要包括以下方面:
- 应用拆分:基于业务域的应用拆分策略
- 独立部署:各个微应用的独立开发、构建和部署
- 运行时集成:微应用的加载、通信和生命周期管理
- 共享资源:公共依赖、组件库、工具函数等的共享策略
- 统一管理:路由、状态、权限等的统一管理方案
微服务架构实现
微前端容器
typescript
// 微前端容器类
class MicroFrontendContainer {
private static instance: MicroFrontendContainer;
private apps: Map<string, MicroApp>;
private config: ContainerConfig;
private constructor() {
this.apps = new Map();
this.config = {
sandbox: true,
prefetch: true,
isolation: 'iframe',
timeout: 3000
};
}
// 获取单例实例
static getInstance(): MicroFrontendContainer {
if (!MicroFrontendContainer.instance) {
MicroFrontendContainer.instance = new MicroFrontendContainer();
}
return MicroFrontendContainer.instance;
}
// 注册微应用
registerApp(appConfig: MicroAppConfig): void {
const app = new MicroApp(appConfig);
this.apps.set(appConfig.name, app);
// 预加载配置
if (this.config.prefetch) {
this.prefetchApp(app);
}
}
// 启动微应用
async startApp(name: string): Promise<void> {
const app = this.apps.get(name);
if (!app) {
throw new Error(`App ${name} not found`);
}
try {
// 加载微应用资源
await this.loadApp(app);
// 创建沙箱环境
const sandbox = this.createSandbox(app);
// 挂载微应用
await this.mountApp(app, sandbox);
// 初始化通信
this.initCommunication(app);
} catch (error) {
console.error(`Failed to start app ${name}:`, error);
throw error;
}
}
// 停止微应用
async stopApp(name: string): Promise<void> {
const app = this.apps.get(name);
if (!app) {
throw new Error(`App ${name} not found`);
}
try {
// 卸载微应用
await this.unmountApp(app);
// 清理沙箱
this.cleanupSandbox(app);
// 清理资源
this.cleanupResources(app);
} catch (error) {
console.error(`Failed to stop app ${name}:`, error);
throw error;
}
}
// 预加载微应用
private async prefetchApp(app: MicroApp): Promise<void> {
try {
const resources = await this.loadResources(app.config.entry);
app.setResources(resources);
} catch (error) {
console.warn(`Failed to prefetch app ${app.config.name}:`, error);
}
}
// 加载微应用资源
private async loadApp(app: MicroApp): Promise<void> {
if (app.isLoaded()) {
return;
}
const resources = app.getResources() ||
await this.loadResources(app.config.entry);
await this.injectResources(resources);
app.setLoaded(true);
}
// 加载资源
private async loadResources(entry: string): Promise<AppResources> {
const response = await fetch(entry);
const html = await response.text();
return {
scripts: this.extractScripts(html),
styles: this.extractStyles(html),
templates: this.extractTemplates(html)
};
}
// 注入资源
private async injectResources(resources: AppResources): Promise<void> {
// 注入样式
await Promise.all(
resources.styles.map(style => this.loadStyle(style))
);
// 注入脚本
await Promise.all(
resources.scripts.map(script => this.loadScript(script))
);
}
// 创建沙箱环境
private createSandbox(app: MicroApp): Sandbox {
if (this.config.isolation === 'iframe') {
return new IframeSandbox(app);
} else {
return new JsSandbox(app);
}
}
// 挂载微应用
private async mountApp(
app: MicroApp,
sandbox: Sandbox
): Promise<void> {
const mountPoint = document.querySelector(app.config.container);
if (!mountPoint) {
throw new Error(
`Mount point ${app.config.container} not found`
);
}
// 执行生命周期钩子
await app.beforeMount();
// 在沙箱中执行挂载
await sandbox.mount(mountPoint);
// 更新应用状态
app.setMounted(true);
// 执行生命周期钩子
await app.afterMount();
}
// 卸载微应用
private async unmountApp(app: MicroApp): Promise<void> {
if (!app.isMounted()) {
return;
}
// 执行生命周期钩子
await app.beforeUnmount();
// 移除DOM
const container = document.querySelector(app.config.container);
if (container) {
container.innerHTML = '';
}
// 更新应用状态
app.setMounted(false);
// 执行生命周期钩子
await app.afterUnmount();
}
// 清理沙箱
private cleanupSandbox(app: MicroApp): void {
const sandbox = app.getSandbox();
if (sandbox) {
sandbox.cleanup();
}
}
// 清理资源
private cleanupResources(app: MicroApp): void {
const resources = app.getResources();
if (resources) {
// 移除样式
resources.styles.forEach(style => {
const element = document.querySelector(
`link[href="${style}"]`
);
element?.remove();
});
// 移除脚本
resources.scripts.forEach(script => {
const element = document.querySelector(
`script[src="${script}"]`
);
element?.remove();
});
}
}
// 初始化应用间通信
private initCommunication(app: MicroApp): void {
const eventBus = EventBus.getInstance();
// 注册应用通信处理器
app.setMessageHandler(message => {
eventBus.emit(`${app.config.name}:message`, message);
});
// 监听其他应用消息
this.apps.forEach(otherApp => {
if (otherApp !== app) {
eventBus.on(
`${otherApp.config.name}:message`,
message => {
app.postMessage(message);
}
);
}
});
}
}
// 微应用类
class MicroApp {
private loaded: boolean = false;
private mounted: boolean = false;
private resources: AppResources | null = null;
private sandbox: Sandbox | null = null;
private messageHandler: MessageHandler | null = null;
constructor(public config: MicroAppConfig) {}
// 生命周期钩子
async beforeMount(): Promise<void> {
await this.invokeLifecycle('beforeMount');
}
async afterMount(): Promise<void> {
await this.invokeLifecycle('afterMount');
}
async beforeUnmount(): Promise<void> {
await this.invokeLifecycle('beforeUnmount');
}
async afterUnmount(): Promise<void> {
await this.invokeLifecycle('afterUnmount');
}
// 调用生命周期函数
private async invokeLifecycle(name: string): Promise<void> {
const lifecycle = (window as any)[
`${this.config.name}:${name}`
];
if (typeof lifecycle === 'function') {
await lifecycle();
}
}
// 状态管理
isLoaded(): boolean {
return this.loaded;
}
setLoaded(loaded: boolean): void {
this.loaded = loaded;
}
isMounted(): boolean {
return this.mounted;
}
setMounted(mounted: boolean): void {
this.mounted = mounted;
}
// 资源管理
getResources(): AppResources | null {
return this.resources;
}
setResources(resources: AppResources): void {
this.resources = resources;
}
// 沙箱管理
getSandbox(): Sandbox | null {
return this.sandbox;
}
setSandbox(sandbox: Sandbox): void {
this.sandbox = sandbox;
}
// 消息通信
setMessageHandler(handler: MessageHandler): void {
this.messageHandler = handler;
}
postMessage(message: any): void {
this.messageHandler?.(message);
}
}
// 沙箱基类
abstract class Sandbox {
constructor(protected app: MicroApp) {}
abstract mount(container: Element): Promise<void>;
abstract cleanup(): void;
}
// iframe沙箱
class IframeSandbox extends Sandbox {
private iframe: HTMLIFrameElement | null = null;
async mount(container: Element): Promise<void> {
this.iframe = document.createElement('iframe');
this.iframe.src = 'about:blank';
this.iframe.style.width = '100%';
this.iframe.style.height = '100%';
this.iframe.style.border = 'none';
container.appendChild(this.iframe);
// 注入资源到iframe
await this.injectResources();
}
cleanup(): void {
this.iframe?.remove();
this.iframe = null;
}
private async injectResources(): Promise<void> {
if (!this.iframe) return;
const resources = this.app.getResources();
if (!resources) return;
const doc = this.iframe.contentDocument;
if (!doc) return;
// 注入样式
resources.styles.forEach(style => {
const link = doc.createElement('link');
link.rel = 'stylesheet';
link.href = style;
doc.head.appendChild(link);
});
// 注入脚本
for (const script of resources.scripts) {
await new Promise((resolve, reject) => {
const scriptElement = doc.createElement('script');
scriptElement.src = script;
scriptElement.onload = resolve;
scriptElement.onerror = reject;
doc.head.appendChild(scriptElement);
});
}
}
}
// JS沙箱
class JsSandbox extends Sandbox {
private proxy: Window | null = null;
async mount(container: Element): Promise<void> {
// 创建代理对象
this.proxy = new Proxy(window, {
get: (target, property) => {
// 处理特殊属性
if (this.isProtected(property)) {
return target[property as keyof Window];
}
// 返回沙箱中的值
return (this.app as any)[property];
},
set: (target, property, value) => {
// 禁止修改保护属性
if (this.isProtected(property)) {
return false;
}
// 设置值到沙箱
(this.app as any)[property] = value;
return true;
}
});
// 在沙箱环境中执行代码
this.executeInSandbox(() => {
const resources = this.app.getResources();
if (!resources) return;
// 执行脚本
resources.scripts.forEach(script => {
const scriptElement = document.createElement('script');
scriptElement.src = script;
container.appendChild(scriptElement);
});
});
}
cleanup(): void {
this.proxy = null;
}
private executeInSandbox(code: Function): void {
if (!this.proxy) return;
// 保存原始window
const originalWindow = window;
// 替换为代理对象
(window as any) = this.proxy;
try {
// 执行代码
code();
} finally {
// 恢复原始window
(window as any) = originalWindow;
}
}
private isProtected(property: string | symbol): boolean {
// 保护的全局属性列表
const protectedProps = [
'window',
'document',
'location',
'history'
];
return protectedProps.includes(property.toString());
}
}
// 事件总线
class EventBus {
private static instance: EventBus;
private handlers: Map<string, Set<Function>>;
private constructor() {
this.handlers = new Map();
}
static getInstance(): EventBus {
if (!EventBus.instance) {
EventBus.instance = new EventBus();
}
return EventBus.instance;
}
on(event: string, handler: Function): void {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)?.add(handler);
}
off(event: string, handler: Function): void {
this.handlers.get(event)?.delete(handler);
}
emit(event: string, data?: any): void {
this.handlers.get(event)?.forEach(handler => {
handler(data);
});
}
}
// 接口定义
interface ContainerConfig {
sandbox: boolean;
prefetch: boolean;
isolation: 'iframe' | 'js';
timeout: number;
}
interface MicroAppConfig {
name: string;
entry: string;
container: string;
props?: Record<string, any>;
}
interface AppResources {
scripts: string[];
styles: string[];
templates: string[];
}
type MessageHandler = (message: any) => void;
// 使用示例
const container = MicroFrontendContainer.getInstance();
// 注册微应用
container.registerApp({
name: 'app1',
entry: 'http://localhost:3001',
container: '#app1'
});
container.registerApp({
name: 'app2',
entry: 'http://localhost:3002',
container: '#app2'
});
// 启动微应用
await container.startApp('app1');
// 停止微应用
await container.stopApp('app1');
最佳实践与建议
-
应用拆分
- 基于业务域划分
- 合理粒度
- 独立演进
- 技术栈灵活
-
依赖管理
- 共享依赖
- 版本控制
- 构建优化
- 运行时加载
-
通信机制
- 事件总线
- 状态共享
- 数据隔离
- 安全控制
-
部署策略
- 独立部署
- 灰度发布
- 版本控制
- 回滚机制
总结
前端微服务架构需要考虑以下方面:
- 应用拆分和集成策略
- 资源加载和性能优化
- 应用通信和状态管理
- 部署和运维支持
- 开发和协作流程
通过合理的微服务架构设计,可以提高前端应用的可维护性和扩展性。
学习资源
- 微前端架构设计
- 模块联邦实践
- 沙箱隔离方案
- 性能优化指南
- 部署运维实践
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻