工程化与框架系列(35)--前端微服务架构实践

前端微服务架构实践 🏗️

引言

随着前端应用规模的不断扩大,微服务架构在前端领域的应用越来越广泛。本文将深入探讨前端微服务架构的实现方案、最佳实践和相关工具。

微服务架构概述

前端微服务架构主要包括以下方面:

  • 应用拆分:基于业务域的应用拆分策略
  • 独立部署:各个微应用的独立开发、构建和部署
  • 运行时集成:微应用的加载、通信和生命周期管理
  • 共享资源:公共依赖、组件库、工具函数等的共享策略
  • 统一管理:路由、状态、权限等的统一管理方案

微服务架构实现

微前端容器

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');

最佳实践与建议

  1. 应用拆分

    • 基于业务域划分
    • 合理粒度
    • 独立演进
    • 技术栈灵活
  2. 依赖管理

    • 共享依赖
    • 版本控制
    • 构建优化
    • 运行时加载
  3. 通信机制

    • 事件总线
    • 状态共享
    • 数据隔离
    • 安全控制
  4. 部署策略

    • 独立部署
    • 灰度发布
    • 版本控制
    • 回滚机制

总结

前端微服务架构需要考虑以下方面:

  1. 应用拆分和集成策略
  2. 资源加载和性能优化
  3. 应用通信和状态管理
  4. 部署和运维支持
  5. 开发和协作流程

通过合理的微服务架构设计,可以提高前端应用的可维护性和扩展性。

学习资源

  1. 微前端架构设计
  2. 模块联邦实践
  3. 沙箱隔离方案
  4. 性能优化指南
  5. 部署运维实践

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

相关推荐
随风九天35 分钟前
使用 Nginx 进行前端灰度发布的策略与实践
运维·前端·nginx·前端灰度发布
黄Java1 小时前
SVG中linearGradient的id冲突的显隐问题深度解析
前端·svg
蜗牛快跑1232 小时前
通过尤大“围绕Vite的前端统一框架”分享,看未来前端发展趋势
前端
skywalk81632 小时前
Mac下安装Zed以及Zed对MCP(模型上下文协议)的支持
服务器·前端·macos
陈龙龙的陈龙龙2 小时前
macOS 安装 Homebrew、nvm 及安装切换 node 版本
前端·macos·bash
asphyxia2 小时前
老龄化项目问题解决
前端
SaebaRyo2 小时前
作为一个前端er如何了解LLM(大语言模型)
前端·llm
悬炫2 小时前
深入解析浏览器渲染原理与性能优化策略
前端·javascript
鸡血园地2 小时前
前端性能优化
前端