JavaScript系列(33)--微前端架构详解
JavaScript微前端架构详解 🏗️
今天,让我们深入了解JavaScript的微前端架构,这是一种用于构建和管理大型前端应用的现代架构模式。
微前端基础概念 🌟
💡 小知识:微前端是一种将前端应用分解成更小、更易管理的独立部分的架构风格。每个部分可以由不同的团队独立开发、测试和部署,最终组合成一个完整的应用。
基本实现方案 📊
// 1. 基础路由分发
class MicroFrontendRouter {
constructor() {
this.routes = new Map();
this.container = null;
}
setContainer(containerId) {
this.container = document.getElementById(containerId);
}
registerApp(route, loadApp) {
this.routes.set(route, loadApp);
}
async navigateTo(route) {
if (!this.routes.has(route)) {
throw new Error(`Route ${route} not found`);
}
const loadApp = this.routes.get(route);
const app = await loadApp();
this.container.innerHTML = '';
this.container.appendChild(app);
}
}
// 2. 应用加载器
class AppLoader {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.loadedScripts = new Set();
this.loadedStyles = new Set();
}
async loadScript(url) {
if (this.loadedScripts.has(url)) {
return;
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = this.baseUrl + url;
script.onload = () => {
this.loadedScripts.add(url);
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
}
async loadStyle(url) {
if (this.loadedStyles.has(url)) {
return;
}
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = this.baseUrl + url;
link.onload = () => {
this.loadedStyles.add(url);
resolve();
};
link.onerror = reject;
document.head.appendChild(link);
});
}
}
// 3. 沙箱环境
class MicroAppSandbox {
constructor(appName) {
this.appName = appName;
this.windowSnapshot = {};
this.modifiedProps = new Set();
}
activate() {
// 保存当前window状态
this.windowSnapshot = this.snapshotWindow();
// 创建代理对象
return new Proxy(window, {
set: (target, prop, value) => {
if (!this.modifiedProps.has(prop)) {
this.modifiedProps.add(prop);
}
target[prop] = value;
return true;
},
get: (target, prop) => {
return target[prop];
}
});
}
deactivate() {
// 恢复window状态
this.restoreWindow();
this.modifiedProps.clear();
}
snapshotWindow() {
const snapshot = {};
for (const prop in window) {
if (window.hasOwnProperty(prop)) {
snapshot[prop] = window[prop];
}
}
return snapshot;
}
restoreWindow() {
this.modifiedProps.forEach(prop => {
if (prop in this.windowSnapshot) {
window[prop] = this.windowSnapshot[prop];
} else {
delete window[prop];
}
});
}
}
高级实现模式 🚀
// 1. 通信系统
class MessageBus {
constructor() {
this.subscribers = new Map();
}
subscribe(topic, callback, appName) {
if (!this.subscribers.has(topic)) {
this.subscribers.set(topic, new Map());
}
const topicSubscribers = this.subscribers.get(topic);
if (!topicSubscribers.has(appName)) {
topicSubscribers.set(appName, new Set());
}
topicSubscribers.get(appName).add(callback);
return () => {
const callbacks = topicSubscribers.get(appName);
callbacks.delete(callback);
if (callbacks.size === 0) {
topicSubscribers.delete(appName);
}
};
}
publish(topic, data, sourceApp) {
if (!this.subscribers.has(topic)) return;
const topicSubscribers = this.subscribers.get(topic);
for (const [appName, callbacks] of topicSubscribers) {
if (appName !== sourceApp) {
callbacks.forEach(callback => callback(data, sourceApp));
}
}
}
}
// 2. 状态管理
class SharedState {
constructor() {
this.state = {};
this.listeners = new Map();
}
setState(path, value, appName) {
const oldValue = this.getState(path);
if (oldValue === value) return;
this.setNestedValue(this.state, path.split('.'), value);
this.notifyListeners(path, value, oldValue, appName);
}
getState(path) {
return this.getNestedValue(this.state, path.split('.'));
}
subscribe(path, callback, appName) {
if (!this.listeners.has(path)) {
this.listeners.set(path, new Map());
}
const pathListeners = this.listeners.get(path);
if (!pathListeners.has(appName)) {
pathListeners.set(appName, new Set());
}
pathListeners.get(appName).add(callback);
return () => {
const callbacks = pathListeners.get(appName);
callbacks.delete(callback);
if (callbacks.size === 0) {
pathListeners.delete(appName);
}
};
}
setNestedValue(obj, path, value) {
const lastKey = path.pop();
const target = path.reduce((obj, key) => {
if (!(key in obj)) obj[key] = {};
return obj[key];
}, obj);
target[lastKey] = value;
}
getNestedValue(obj, path) {
return path.reduce((obj, key) => obj && obj[key], obj);
}
notifyListeners(path, newValue, oldValue, sourceApp) {
if (this.listeners.has(path)) {
const pathListeners = this.listeners.get(path);
for (const [appName, callbacks] of pathListeners) {
if (appName !== sourceApp) {
callbacks.forEach(callback =>
callback(newValue, oldValue)
);
}
}
}
}
}
// 3. 生命周期管理
class LifecycleManager {
constructor() {
this.apps = new Map();
}
registerApp(appName, lifecycles) {
this.apps.set(appName, {
bootstrap: lifecycles.bootstrap || (() => Promise.resolve()),
mount: lifecycles.mount || (() => Promise.resolve()),
unmount: lifecycles.unmount || (() => Promise.resolve()),
update: lifecycles.update || (() => Promise.resolve()),
status: 'NOT_LOADED'
});
}
async bootstrapApp(appName) {
const app = this.apps.get(appName);
if (!app) throw new Error(`App ${appName} not found`);
if (app.status !== 'NOT_LOADED') return;
try {
await app.bootstrap();
app.status = 'NOT_MOUNTED';
} catch (error) {
app.status = 'LOAD_ERROR';
throw error;
}
}
async mountApp(appName) {
const app = this.apps.get(appName);
if (!app) throw new Error(`App ${appName} not found`);
if (app.status !== 'NOT_MOUNTED') return;
try {
await app.mount();
app.status = 'MOUNTED';
} catch (error) {
app.status = 'MOUNT_ERROR';
throw error;
}
}
async unmountApp(appName) {
const app = this.apps.get(appName);
if (!app) throw new Error(`App ${appName} not found`);
if (app.status !== 'MOUNTED') return;
try {
await app.unmount();
app.status = 'NOT_MOUNTED';
} catch (error) {
app.status = 'UNMOUNT_ERROR';
throw error;
}
}
}
实际应用场景 💼
// 1. 微前端容器应用
class MicroFrontendContainer {
constructor() {
this.router = new MicroFrontendRouter();
this.messageBus = new MessageBus();
this.sharedState = new SharedState();
this.lifecycleManager = new LifecycleManager();
}
async registerApp(config) {
const {
name,
route,
loadApp,
lifecycles
} = config;
// 注册路由
this.router.registerApp(route, async () => {
const app = await loadApp();
return app;
});
// 注册生命周期
this.lifecycleManager.registerApp(name, lifecycles);
// 创建沙箱环境
const sandbox = new MicroAppSandbox(name);
// 注册通信接口
const api = {
messageBus: this.messageBus,
sharedState: this.sharedState,
sandbox
};
return api;
}
async start() {
window.addEventListener('popstate', () => {
this.handleRouteChange();
});
await this.handleRouteChange();
}
async handleRouteChange() {
const route = window.location.pathname;
await this.router.navigateTo(route);
}
}
// 2. 微应用实现
class MicroApp {
constructor(name, api) {
this.name = name;
this.messageBus = api.messageBus;
this.sharedState = api.sharedState;
this.sandbox = api.sandbox;
}
async bootstrap() {
// 初始化应用
this.sandbox.activate();
// 订阅消息
this.unsubscribe = this.messageBus.subscribe(
'global',
this.handleMessage.bind(this),
this.name
);
// 监听状态变化
this.unsubscribeState = this.sharedState.subscribe(
'user',
this.handleStateChange.bind(this),
this.name
);
}
async mount() {
// 渲染应用
this.render();
}
async unmount() {
// 清理资源
this.unsubscribe();
this.unsubscribeState();
this.sandbox.deactivate();
}
handleMessage(data, sourceApp) {
console.log(`Received message from ${sourceApp}:`, data);
}
handleStateChange(newValue, oldValue) {
console.log('State changed:', { newValue, oldValue });
this.update();
}
render() {
// 实现具体的渲染逻辑
}
update() {
// 实现更新逻辑
}
}
// 3. 应用间通信示例
class AppCommunication {
constructor(messageBus, sharedState) {
this.messageBus = messageBus;
this.sharedState = sharedState;
}
sendMessage(topic, data, sourceApp) {
this.messageBus.publish(topic, data, sourceApp);
}
updateState(path, value, sourceApp) {
this.sharedState.setState(path, value, sourceApp);
}
}
性能优化技巧 ⚡
// 1. 资源预加载
class ResourcePreloader {
constructor() {
this.preloadQueue = new Map();
this.loading = new Set();
}
addToQueue(resources) {
resources.forEach(resource => {
if (!this.preloadQueue.has(resource.url)) {
this.preloadQueue.set(resource.url, {
type: resource.type,
priority: resource.priority || 0
});
}
});
}
async preload() {
// 按优先级排序
const sortedResources = Array.from(this.preloadQueue.entries())
.sort((a, b) => b[1].priority - a[1].priority);
for (const [url, config] of sortedResources) {
if (this.loading.has(url)) continue;
this.loading.add(url);
try {
if (config.type === 'script') {
await this.preloadScript(url);
} else if (config.type === 'style') {
await this.preloadStyle(url);
}
} finally {
this.loading.delete(url);
this.preloadQueue.delete(url);
}
}
}
preloadScript(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'script';
link.href = url;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
preloadStyle(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = url;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
}
// 2. 应用缓存
class AppCache {
constructor(maxSize = 5) {
this.cache = new Map();
this.maxSize = maxSize;
}
set(appName, instance) {
if (this.cache.size >= this.maxSize) {
const oldestApp = this.cache.keys().next().value;
this.delete(oldestApp);
}
this.cache.set(appName, {
instance,
timestamp: Date.now()
});
}
get(appName) {
const entry = this.cache.get(appName);
if (entry) {
entry.timestamp = Date.now();
return entry.instance;
}
return null;
}
delete(appName) {
const instance = this.cache.get(appName);
if (instance) {
// 清理应用资源
if (instance.unmount) {
instance.unmount();
}
this.cache.delete(appName);
}
}
clear() {
for (const [appName] of this.cache) {
this.delete(appName);
}
}
}
// 3. 性能监控
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
}
startMeasure(appName, phase) {
const key = `${appName}-${phase}`;
performance.mark(`${key}-start`);
}
endMeasure(appName, phase) {
const key = `${appName}-${phase}`;
performance.mark(`${key}-end`);
performance.measure(
key,
`${key}-start`,
`${key}-end`
);
const measures = performance.getEntriesByName(key);
const duration = measures[measures.length - 1].duration;
if (!this.metrics.has(appName)) {
this.metrics.set(appName, new Map());
}
const appMetrics = this.metrics.get(appName);
if (!appMetrics.has(phase)) {
appMetrics.set(phase, []);
}
appMetrics.get(phase).push(duration);
// 清理marks
performance.clearMarks(`${key}-start`);
performance.clearMarks(`${key}-end`);
performance.clearMeasures(key);
}
getMetrics(appName, phase) {
const appMetrics = this.metrics.get(appName);
if (!appMetrics) return null;
const phaseMetrics = appMetrics.get(phase);
if (!phaseMetrics) return null;
return {
average: phaseMetrics.reduce((a, b) => a + b, 0) / phaseMetrics.length,
min: Math.min(...phaseMetrics),
max: Math.max(...phaseMetrics),
count: phaseMetrics.length
};
}
}
最佳实践建议 💡
- 应用隔离
// 1. 样式隔离
class StyleIsolator {
constructor(appName) {
this.appName = appName;
}
wrapStyles(styles) {
return styles.replace(
/([^}]*){([^}]*)}/g,
`[data-app="${this.appName}"] $1{$2}`
);
}
injectStyles(styles) {
const styleElement = document.createElement('style');
styleElement.setAttribute('data-app', this.appName);
styleElement.textContent = this.wrapStyles(styles);
document.head.appendChild(styleElement);
return () => styleElement.remove();
}
}
// 2. 全局变量隔离
class GlobalVarIsolator {
constructor() {
this.originalValues = new Map();
}
isolate(variables) {
variables.forEach(varName => {
if (varName in window) {
this.originalValues.set(varName, window[varName]);
}
window[varName] = undefined;
});
}
restore() {
for (const [varName, value] of this.originalValues) {
if (value === undefined) {
delete window[varName];
} else {
window[varName] = value;
}
}
this.originalValues.clear();
}
}
// 3. 错误边界
class ErrorBoundary {
constructor(appName, onError) {
this.appName = appName;
this.onError = onError;
}
wrap(fn) {
return async (...args) => {
try {
return await fn(...args);
} catch (error) {
this.onError(error, this.appName);
throw error;
}
};
}
}
结语 📝
微前端架构为大型前端应用提供了一种可扩展的解决方案。通过本文,我们学习了:
- 微前端的基本概念和实现方案
- 高级实现模式和通信机制
- 实际应用场景和示例
- 性能优化技巧
- 最佳实践和注意事项
💡 学习建议:在采用微前端架构时,要注意平衡架构的复杂性和业务需求。不是所有项目都需要微前端架构,要根据实际情况选择合适的解决方案。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻