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

news/2025/3/16 5:40:09/

前端微服务架构实践 🏗️

引言

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

微服务架构概述

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

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

微服务架构实现

前端容器

// 微前端容器类
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();// 移除DOMconst 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);// 注入资源到iframeawait 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;// 保存原始windowconst 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. 部署运维实践

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

终身学习,共同成长。

咱们下一期见

💻


http://www.ppmy.cn/news/1579504.html

相关文章

FlinkSQL实现实时同步和实时统计过程(MySQL TO MySQL)

实时同步 注意mysql表的主键要和FlinkSQL的一致 set execution.checkpointing.checkpoints - after - tasks - finish.enabled true; SET pipeline.operator - chaining false; set state.backend.type rocksdb; set execution.checkpointing.interval 8000; set state.c…

RocketMQ面试题:进阶部分

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

Docker+Flask 实战:打造高并发微服务架构

DockerFlask 实战&#xff1a;打造高并发微服务架构 今天我们要深入探讨一个非常热门且实用的主题&#xff1a;基于 Docker 部署 Python Flask 应用。Docker 作为当下最流行的容器化技术&#xff0c;已经广泛应用于各种开发和部署场景&#xff0c;尤其是在微服务架构中。而 Fl…

深入理解 HTML 链接:网页导航的核心元素

在网页开发的广袤领域中&#xff0c;HTML 链接无疑扮演着举足轻重的角色&#xff0c;它是实现网页之间无缝跳转、构建互联网络世界的核心部分。无论是引导用户在不同页面间穿梭&#xff0c;还是关联各类资源&#xff0c;HTML 链接都发挥着关键作用。 一、HTML 链接基础认知 HT…

Pycharm中脚本执行的3种模式——unittest框架、pytest框架及普通模式

一. Python 运行脚本的三种模式 a. unittest 框架 b. pytest 框架 c. 普通模式 二、PyCharm 默认使用 pytest 框架执行 unittest 框架的测试用例 三、如何修改Pycharm的脚本运行的模式? 方法1. 修改 PyCharm 默认的测试框架 方法2. 设置运行脚本时的默认框架 四、mai…

【每日学点HarmonyOS Next知识】拖拽调整列表顺序、tab回弹、自定义弹窗this、状态变量修饰枚举

1、HarmonyOS 功能实现&#xff08;拖拽调整列表顺序&#xff09;&#xff1f; 可参考&#xff1a; import curves from ohos.curves; import Curves from ohos.curvesEntry Component struct ListItemExample {State private arr: number[] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]…

【openGauss】物理备份恢复

文章目录 1. gs_backup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复&#xff08;3&#xff09;手动恢复的办法 2. gs_basebackup&#xff08;1&#xff09;备份&#xff08;2&#xff09;恢复① 伪造数据目录丢失② 恢复 3. gs_probackup&#xff08;1&#xf…

MySQL隐式依赖引发的字段长度溢出:一次触发器事故的深度剖析

MySQL隐式依赖引发的字段长度溢出&#xff1a;一次触发器事故的深度剖析 场景还原&#xff1a;诡异的字段不存在报错 某日接到生产环境报警&#xff0c;发现核心业务表order_main&#xff08;A表&#xff09;的插入操作频繁报错&#xff0c;错误提示却显示ERROR 1406 (22001)…