微前端架构
约 4420 字大约 15 分钟
微前端架构
微前端基础概念 🟢
1. 什么是微前端
微前端是一种将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块的架构风格。这种架构风格主要解决了以下问题:
- 技术栈限制:
- 允许不同团队使用不同的技术栈:每个团队可以根据自己的技术特长和业务需求选择最适合的技术栈
- 支持渐进式升级和重构:可以逐步将旧系统升级到新技术栈,而不需要一次性重写整个应用
- 降低技术选型的限制:新项目可以尝试新技术,不会被原有技术栈所束缚
- 提高团队技术栈的灵活性:不同团队可以专注于自己最擅长的技术领域
- 团队协作:
- 支持多团队并行开发:每个团队可以独立开发自己的模块,不会互相阻塞
- 降低沟通成本:团队之间通过明确的接口进行通信,减少不必要的沟通
- 明确团队职责边界:每个团队负责特定的业务模块,责任明确
- 提高开发效率:避免了大型单体应用带来的开发效率瓶颈
- 应用维护:
- 降低应用复杂度:将大型应用拆分成小型应用,每个应用更容易理解和维护
- 简化代码维护:每个微应用的代码量较小,更容易维护和测试
- 支持独立部署:每个微应用可以独立部署,不影响其他应用
- 提高系统可维护性:问题隔离,某个模块的问题不会影响整个系统
2. 核心价值
- 技术价值:
增量升级:
- 可以逐步升级现有系统
- 降低升级风险
- 保证业务连续性
- 验证新技术的可行性
独立部署:
- 每个子应用独立部署
- 降低部署风险
- 提高发布效率
- 支持灰度发布
技术栈无关:
- 支持不同技术栈共存
- 充分利用已有技术积累
- 降低学习成本
- 提高团队积极性
隔离性:
- 子应用间实现良好的隔离
- CSS样式隔离
- JavaScript运行时隔离
- 错误隔离
- 业务价值:
团队自治:
- 团队可以独立开发和维护
- 提高团队责任心
- 加快决策速度
- 提高团队积极性
复用性:
- 可以在不同项目间复用子应用
- 降低开发成本
- 提高开发效率
- 保证功能一致性
扩展性:
- 系统易于扩展和维护
- 支持业务快速迭代
- 适应业务变化
- 降低维护成本
灵活性:
- 支持业务的快速变化
- 快速响应市场需求
- 降低试错成本
- 提高竞争力
实现方案详解 🟡
1. 基于qiankun的实现
1.1 基座应用配置
基座应用是整个微前端应用的入口,负责子应用的注册、加载和管理。
import { registerMicroApps, start } from 'qiankun';
// 注册子应用
registerMicroApps([
{
name: 'vue-app', // 子应用名称
entry: '//localhost:8081', // 子应用入口
container: '#vue-container', // 子应用挂载点
activeRule: '/vue', // 激活规则
props: { // 传递给子应用的数据
shared: {
utils: {/* 共享工具方法 */},
state: {/* 共享状态 */}
}
}
},
{
name: 'react-app',
entry: '//localhost:8082',
container: '#react-container',
activeRule: '/react'
}
]);
// 启动应用
start({
prefetch: true, // 预加载
sandbox: {
strictStyleIsolation: true, // 严格的样式隔离
experimentalStyleIsolation: true // 实验性的样式隔离
},
singular: true // 单实例模式
});
1.2 子应用配置
子应用需要导出特定的生命周期钩子,以供主应用调用。
const { name } = require('./package.json');
module.exports = {
devServer: {
port: 8081,
headers: {
'Access-Control-Allow-Origin': '*' // 允许跨域访问
}
},
configureWebpack: {
output: {
library: `${name}-[name]`, // 导出库的名称
libraryTarget: 'umd', // 导出库的类型
jsonpFunction: `webpackJsonp_${name}` // 防止冲突
}
}
};
// 入口文件
import { createApp } from 'vue';
import App from './App.vue';
let instance = null;
// 导出生命周期钩子
export async function bootstrap() {
console.log('vue app bootstraped');
}
export async function mount(props) {
instance = createApp(App);
// 处理props
if (props) {
instance.config.globalProperties.$shared = props.shared;
}
instance.mount(props.container);
}
export async function unmount() {
instance.unmount();
instance = null;
}
2. 基于Module Federation的实现
2.1 基座应用配置
Module Federation是Webpack 5提供的一个功能,允许多个独立的构建之间共享代码。
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
filename: 'remoteEntry.js',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
2.2 子应用配置
子应用需要配置要暴露的模块和共享的依赖。
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./Header': './src/components/Header'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
};
3. 自定义实现方案
3.1 路由分发
实现一个路由分发系统,根据URL加载不同的子应用。
class RouterManager {
constructor() {
this.apps = new Map();
this.currentApp = null;
// 监听路由变化
window.addEventListener('popstate', this.handleRouteChange.bind(this));
}
// 注册子应用
registerApp(name, entry, container, routePrefix) {
this.apps.set(routePrefix, {
name,
entry,
container,
instance: null
});
}
// 处理路由变化
async handleRouteChange() {
const path = window.location.pathname;
for (const [prefix, app] of this.apps.entries()) {
if (path.startsWith(prefix)) {
await this.loadAndMountApp(app);
break;
}
}
}
// 加载并挂载应用
async loadAndMountApp(app) {
if (this.currentApp) {
await this.unmountCurrentApp();
}
if (!app.instance) {
app.instance = await this.loadApp(app.entry);
}
await app.instance.mount(document.querySelector(app.container));
this.currentApp = app;
}
}
应用通信详解 🔴
1. 基于事件的通信机制
事件通信是微前端中最基础也是最常用的通信方式。它具有以下特点:
- 松耦合:发布者和订阅者之间没有直接依赖
- 灵活性:可以动态添加和移除监听器
- 实时性:事件触发后立即执行
- 多对多:支持多个发布者和订阅者
1.1 全局事件总线实现
全局事件总线需要考虑以下几个关键点:
- 事件的注册和注销
- 事件的优先级处理
- 错误处理机制
- 内存泄漏防范
- 事件的命名空间
具体实现:
class EventBus {
constructor() {
// 存储所有事件监听器
this.listeners = new Map();
// 存储一次性事件监听器
this.onceListeners = new Map();
// 事件历史��,用于新注册的监听器可以收到之前的事件
this.eventHistory = new Map();
// 最大历史记录数
this.maxHistorySize = 100;
}
// 注册事件监听器
on(event, callback, options = {}) {
const {
scope = 'global', // 事件作用域
priority = 0, // 事件优先级
async = false, // 是否异步执行
timeout = 3000 // 异步执行超时时间
} = options;
if (!this.listeners.has(event)) {
this.listeners.set(event, new Map());
}
const eventListeners = this.listeners.get(event);
if (!eventListeners.has(scope)) {
eventListeners.set(scope, []);
}
eventListeners.get(scope).push({
callback,
priority,
async,
timeout
});
// 按优先级排序
eventListeners.get(scope).sort((a, b) => b.priority - a.priority);
// 发送历史事件
if (this.eventHistory.has(event)) {
const history = this.eventHistory.get(event);
history.forEach(data => this.executeCallback(callback, data, async, timeout));
}
// 返回取消订阅的函数
return () => this.off(event, callback, scope);
}
// 发布事件
emit(event, data, options = {}) {
const {
scope = 'global',
recordHistory = true
} = options;
// 记录事件历史
if (recordHistory) {
if (!this.eventHistory.has(event)) {
this.eventHistory.set(event, []);
}
const history = this.eventHistory.get(event);
history.push(data);
if (history.length > this.maxHistorySize) {
history.shift();
}
}
if (!this.listeners.has(event)) return;
const eventListeners = this.listeners.get(event);
if (scope === 'global') {
// 触发所有作用域的监听器
for (const [, listeners] of eventListeners) {
this.executeListeners(listeners, data);
}
} else if (eventListeners.has(scope)) {
// 只触发特定作用域的监听器
this.executeListeners(eventListeners.get(scope), data);
}
}
// 执行监听器列表
async executeListeners(listeners, data) {
for (const listener of listeners) {
await this.executeCallback(
listener.callback,
data,
listener.async,
listener.timeout
);
}
}
// 执行单个回调
async executeCallback(callback, data, async, timeout) {
try {
if (async) {
await Promise.race([
callback(data),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
} else {
callback(data);
}
} catch (error) {
console.error('Event execution error:', error);
}
}
// 清理事件监听器
clear(event, scope = 'global') {
if (event) {
if (this.listeners.has(event)) {
if (scope === 'global') {
this.listeners.delete(event);
} else {
const eventListeners = this.listeners.get(event);
eventListeners.delete(scope);
}
}
} else {
this.listeners.clear();
}
}
}
2. 基于Props的通信
Props通信是最直接的通信方式,主要用于父子应用间的数据传递。在使用时需要注意以下几点:
- 数据流向:
- 单向数据流,从父应用向子应用传递
- 子应用通过回调函数向父应用通信
- 避免双向数据绑定
- 数据类型:
- 基本类型数据
- 复杂对象数据
- 函数回调
- 共享状态
具体实现:
// 主应用配置
class MainApp {
constructor() {
this.state = {
user: null,
theme: 'light',
permissions: []
};
this.childApps = new Map();
}
// 注册子应用
registerApp(name, props = {}) {
const defaultProps = {
// 基础数据
appName: name,
env: process.env.NODE_ENV,
// 共享状态
state: this.state,
// 状态更新方法
setState: this.setState.bind(this),
// 工具函数
utils: {
format: this.format.bind(this),
validate: this.validate.bind(this)
},
// 生命周期钩子
onMount: this.handleChildMount.bind(this),
onUnmount: this.handleChildUnmount.bind(this),
// 事件处理
onEvent: this.handleChildEvent.bind(this)
};
// 合并props
const mergedProps = {
...defaultProps,
...props
};
this.childApps.set(name, {
props: mergedProps,
instance: null
});
return mergedProps;
}
// 状态更新
setState(partial, options = {}) {
const { source, sync = false } = options;
// 更新状态
this.state = {
...this.state,
...partial
};
// 通知其他子应用
this.childApps.forEach((app, name) => {
if (name !== source && app.instance) {
if (sync) {
app.instance.updateState(this.state);
} else {
Promise.resolve().then(() => {
app.instance.updateState(this.state);
});
}
}
});
}
// 处理子应用事件
handleChildEvent(event, data, source) {
console.log(`Event from ${source}:`, event, data);
// 处理子应用事件...
}
}
3. 基于状态管理的通信
对于复杂应用,我们需要一个统一的状态管理方案:
3.1 共享状态管理:
class GlobalState {
constructor(initialState = {}) {
this.state = initialState;
this.listeners = new Set();
this.snapshots = [];
this.maxSnapshots = 10;
}
// 获取状态
getState() {
return { ...this.state };
}
// 更新状态
setState(partial, options = {}) {
const { scope = 'global', recordHistory = true } = options;
// 记录历史
if (recordHistory) {
this.snapshots.push({
state: { ...this.state },
timestamp: Date.now(),
scope
});
// 限制历史记录数量
if (this.snapshots.length > this.maxSnapshots) {
this.snapshots.shift();
}
}
// 更新状态
this.state = {
...this.state,
[scope]: {
...this.state[scope],
...partial
}
};
// 通知监听器
this.notify();
}
// 订阅状态变化
subscribe(listener) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
// 通知所有监听器
notify() {
this.listeners.forEach(listener => {
try {
listener(this.state);
} catch (error) {
console.error('State change listener error:', error);
}
});
}
// 回滚到指定状态
rollback(index) {
if (index >= 0 && index < this.snapshots.length) {
this.state = { ...this.snapshots[index].state };
this.snapshots = this.snapshots.slice(0, index);
this.notify();
}
}
// 清理状态
clear(scope) {
if (scope) {
delete this.state[scope];
} else {
this.state = {};
}
this.notify();
}
}
应用隔离详解 🔴
1. JavaScript沙箱机制
JavaScript沙箱是微前端中最关键的技术之一,它确保了各个子应用之间的JavaScript运行环境的隔离。主要解决以下问题:
- 全局变量冲突:
- 不同子应用可能使用相同的全局变量名
- 子应用可能污染全局环境
- 全局环境的变化可能影响子应用的运行
- 需要确保每个应用有独立的全局上下文
- 事件监听冲突:
- 多个应用可能监听相同的全局事件
- 事件解绑可能影响其他应用
- 需要实现事件监听的独立管理
- 确保应用卸载时正确清理事件监听
- 定时器管理:
- setTimeout/setInterval的管理
- 确保应用卸载时清理定时器
- 防止内存泄漏
- 实现定时器的独立管理
具体实现方案:
class JavaScriptSandbox {
constructor() {
// 存储原始window对象
this.originalWindow = window;
// 代理对象
this.fakeWindow = {};
// 存储修改记录
this.modifyMap = new Map();
// 存储事件监听
this.eventListeners = new Map();
// 存储定时器
this.timers = new Set();
}
// 激活沙箱
active() {
// 创建代理对象
this.sandboxWindow = new Proxy(this.fakeWindow, {
get: (target, property) => {
// 优先返回自身属性
if (property in target) {
return target[property];
}
// 否则返回原始window属性
return this.originalWindow[property];
},
set: (target, property, value) => {
// 记录修改
this.modifyMap.set(property, {
type: 'set',
value
});
target[property] = value;
return true;
}
});
// 重写定时器方法
this.overwriteTimers();
// 重写事件监听方法
this.overwriteEventListeners();
return this.sandboxWindow;
}
// 清理沙箱
inactive() {
// 清理修改的属性
this.modifyMap.forEach((record, property) => {
if (record.type === 'set') {
delete this.fakeWindow[property];
}
});
this.modifyMap.clear();
// 清理事件监听
this.eventListeners.forEach((listeners, type) => {
listeners.forEach(listener => {
this.originalWindow.removeEventListener(type, listener);
});
});
this.eventListeners.clear();
// 清理定时器
this.timers.forEach(timer => {
clearTimeout(timer);
clearInterval(timer);
});
this.timers.clear();
}
// 重写定时器方法
overwriteTimers() {
const sandbox = this;
this.sandboxWindow.setTimeout = function(callback, delay, ...args) {
const timer = sandbox.originalWindow.setTimeout(callback, delay, ...args);
sandbox.timers.add(timer);
return timer;
};
this.sandboxWindow.setInterval = function(callback, delay, ...args) {
const timer = sandbox.originalWindow.setInterval(callback, delay, ...args);
sandbox.timers.add(timer);
return timer;
};
}
// 重写事件监听方法
overwriteEventListeners() {
const sandbox = this;
this.sandboxWindow.addEventListener = function(type, listener, options) {
if (!sandbox.eventListeners.has(type)) {
sandbox.eventListeners.set(type, new Set());
}
sandbox.eventListeners.get(type).add(listener);
sandbox.originalWindow.addEventListener(type, listener, options);
};
this.sandboxWindow.removeEventListener = function(type, listener, options) {
if (sandbox.eventListeners.has(type)) {
sandbox.eventListeners.get(type).delete(listener);
}
sandbox.originalWindow.removeEventListener(type, listener, options);
};
}
}
2. CSS隔离方案
CSS隔离是微前端中另一个重要的技术点,需要解决以下问题:
- 样式冲突问题:
- 不同应用间的样式可能互相影响
- 全局样式可能被覆盖
- 选择器优先级问题
- 需要确保样式的模块化
- 动态样式处理:
- 动态加载的样式如何隔离
- 样式热更新的处理
- 主题切换的实现
- 确保样式的���需加载
实现方案:
class StyleIsolation {
constructor(options = {}) {
this.options = {
prefix: 'app', // 样式前缀
useScope: true, // 是否使用作用域
useShadowDOM: false, // 是否使用Shadow DOM
...options
};
// 存储处理过的样式表
this.styleCache = new Map();
}
// 处理样式
processStyles(styles, appName) {
// 如果已经处理过,直接返回缓存
const cacheKey = `${appName}:${styles}`;
if (this.styleCache.has(cacheKey)) {
return this.styleCache.get(cacheKey);
}
let processedStyles = styles;
if (this.options.useScope) {
// 添加作用域
processedStyles = this.addScope(processedStyles, appName);
}
// 添加前缀
processedStyles = this.addPrefix(processedStyles, appName);
// 缓存处理结果
this.styleCache.set(cacheKey, processedStyles);
return processedStyles;
}
// 添加作用域
addScope(styles, appName) {
return styles.replace(/([^}{]*){([^}]*)}/g, (match, selector, rules) => {
const scopedSelector = selector
.split(',')
.map(s => `[data-app="${appName}"] ${s.trim()}`)
.join(',');
return `${scopedSelector}{${rules}}`;
});
}
// 添加前缀
addPrefix(styles, appName) {
const prefix = `${this.options.prefix}-${appName}`;
return styles.replace(/([^}{]*){([^}]*)}/g, (match, selector, rules) => {
const prefixedSelector = selector
.split(',')
.map(s => `.${prefix} ${s.trim()}`)
.join(',');
return `${prefixedSelector}{${rules}}`;
});
}
// 创建样式元素
createStyleElement(styles, appName) {
const styleElement = document.createElement('style');
styleElement.textContent = this.processStyles(styles, appName);
styleElement.setAttribute('data-app', appName);
return styleElement;
}
// 使用Shadow DOM
createShadowDOM(container, appName) {
const shadow = container.attachShadow({ mode: 'open' });
const styleElement = document.createElement('style');
styleElement.textContent = this.processStyles(styles, appName);
shadow.appendChild(styleElement);
return shadow;
}
}
微前端实践指南 🔴
1. 应用间通信进阶
微前端应用间的通信是一个复杂的话题,需要考虑以下几个关键点:
3.1 通信方式选择
- 基于事件的通信:
- 优点:松耦合、灵活性高
- 缺点:事件管理复杂、调试困难
- 适用场景:异步通信、广播类消息
- 实现要点:事件总线、命名空间隔离
- 基于Props的通信:
- 优点:直观、类型安全
- 缺点:耦合度高、层级深时不便
- 适用场景:父子应用间直接通信
- 实现要点:类型定义、数据验证
- 基于状态共享:
- 优点:统一状态管理、便于调试
- 缺点:实现复杂、性能开销大
- 适用场景:多应用共享数据
- 实现要点:状态同步、冲突处理
具体实现示例:
// 高级事件总线实现
class EventBus {
constructor() {
this.events = new Map();
this.middlewares = [];
}
// 添加中间件
use(middleware) {
if (typeof middleware !== 'function') {
throw new Error('中间件必须是函数');
}
this.middlewares.push(middleware);
}
// 发布事件
async emit(event, data, options = {}) {
const { scope = 'global', sync = false } = options;
// 执行中间件链
let processedData = data;
for (const middleware of this.middlewares) {
try {
processedData = await middleware(event, processedData, scope);
} catch (error) {
console.error('中间件执行错误:', error);
throw error;
}
}
// 获取事件处理器
const handlers = this.events.get(event);
if (!handlers) return;
// 执行处理器
const promises = [];
handlers.forEach(handler => {
if (handler.scope === scope || handler.scope === 'global') {
const promise = handler.callback(processedData);
if (sync) {
promises.push(promise);
}
}
});
if (sync && promises.length > 0) {
await Promise.all(promises);
}
}
// 订阅事件
on(event, callback, options = {}) {
const { scope = 'global', once = false } = options;
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
const handler = {
callback,
scope,
once
};
this.events.get(event).add(handler);
// 返回取消订阅函数
return () => {
const handlers = this.events.get(event);
if (handlers) {
handlers.delete(handler);
if (handlers.size === 0) {
this.events.delete(event);
}
}
};
}
}
3.2 数据同步策略
在微前端架构中,数据同步是一个关键问题,需要考虑以下几个方面:
- 状态同步机制:
- 定期同步:适合不频繁变化的数据
- 实时同步:适合需要即时反应的数据
- 增量同步:适合大量数据的高效同步
- 按需同步:根据业务需求选择同步时机
- 数据一致性保证:
- 版本控制:使用版本号跟踪数据变化
- 冲突解决:处理并发更新的冲突
- 回滚机制:支持数据回滚到之前状态
- 数据验证:确保数据的完整性和正确性
具体实现示例:
// 状态同步管理器
class StateManager {
constructor(options = {}) {
this.options = {
syncInterval: 1000, // 同步间隔
maxRetries: 3, // 最大重试次数
...options
};
this.state = new Map();
this.version = 0;
this.subscribers = new Set();
this.pendingUpdates = new Map();
}
// 更新状态
async setState(key, value, options = {}) {
const { immediate = false } = options;
// 记录更新
this.pendingUpdates.set(key, {
value,
version: ++this.version,
timestamp: Date.now()
});
// 是否立即同步
if (immediate) {
await this.sync();
}
}
// 同步状态
async sync() {
if (this.pendingUpdates.size === 0) return;
try {
// 准备同步数据
const updates = Array.from(this.pendingUpdates.entries())
.map(([key, update]) => ({
key,
...update
}));
// 执行同步
await this.broadcastUpdates(updates);
// 清理已同步的更新
this.pendingUpdates.clear();
} catch (error) {
console.error('状态同步失败:', error);
// 实现重试逻辑
await this.retrySync();
}
}
// 广播更新
async broadcastUpdates(updates) {
const promises = Array.from(this.subscribers).map(subscriber =>
subscriber(updates)
);
await Promise.all(promises);
}
// 订阅更新
subscribe(callback) {
this.subscribers.add(callback);
return () => this.subscribers.delete(callback);
}
// 重试同步
async retrySync(retryCount = 0) {
if (retryCount >= this.options.maxRetries) {
throw new Error('同步重试次数超过限制');
}
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, retryCount) * 1000)
);
try {
await this.sync();
} catch (error) {
await this.retrySync(retryCount + 1);
}
}
}
2. 性能优化策略
在微前端架构中,性能优化是一个重要的话题,需要从多个层面进行考虑:
- 加载性能优化:
- 应用预加载:提前加载可能用到的子应用
- 资源共享:合理共享公共资源,避免重复加载
- 按需加载:实现子应用的按需加载策略
- 缓存优化:合理利用浏览器缓存机制
- 运行时性能优化:
- 沙箱优化:减少沙箱创建和切换的开销
- 内存管理:及时清理不需要的资源
- 事件管理:优化事件监听和解绑
- 渲染性能:优化DOM操作和渲染策略
具体实现示例:
// 应用加载器实现
class AppLoader {
constructor(options = {}) {
this.options = {
maxConcurrent: 2,
timeout: 10000,
retryTimes: 3,
...options
};
this.loadingApps = new Map();
this.loadedApps = new Map();
this.queue = [];
}
// 加载应用
async loadApp(appConfig) {
// 检查缓存
if (this.loadedApps.has(appConfig.name)) {
return this.loadedApps.get(appConfig.name);
}
// 检查是否正在加载
if (this.loadingApps.has(appConfig.name)) {
return this.loadingApps.get(appConfig.name);
}
// 创建加载Promise
const loadPromise = this.createLoadPromise(appConfig);
this.loadingApps.set(appConfig.name, loadPromise);
try {
const app = await loadPromise;
this.loadedApps.set(appConfig.name, app);
this.loadingApps.delete(appConfig.name);
return app;
} catch (error) {
this.loadingApps.delete(appConfig.name);
throw error;
}
}
// 创建加载Promise
createLoadPromise(appConfig) {
let retryCount = 0;
const load = async () => {
try {
// 加载资源
const resources = await this.loadResources(appConfig);
// 初始化应用
const app = await this.initializeApp(appConfig, resources);
return app;
} catch (error) {
if (retryCount < this.options.retryTimes) {
retryCount++;
return load();
}
throw error;
}
};
return load();
}
// 预加载应用
preloadApp(appConfig) {
if (this.loadingApps.size >= this.options.maxConcurrent) {
this.queue.push(appConfig);
return;
}
this.loadApp(appConfig).catch(error => {
console.error(`预加载应用 ${appConfig.name} 失败:`, error);
});
}
// 资源加载优化
async loadResources(appConfig) {
const resources = new Map();
// 并行加载资源
await Promise.all(
appConfig.resources.map(async resource => {
const content = await this.loadResource(resource);
resources.set(resource.name, content);
})
);
return resources;
}
// 单个资源加载
async loadResource(resource) {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, this.options.timeout);
try {
const response = await fetch(resource.url, {
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.text();
} finally {
clearTimeout(timeoutId);
}
}
}
[未完待续...]