微前端中微内核&插件化思想的应用
前言
微前端作为一种新兴的前端架构模式,其核心思想深度体现了微内核(Microkernel)和插件化(Plugin-based)的设计哲学。通过将大型单体前端应用分解为多个独立的微应用,微前端实现了团队独立开发、技术栈多样化和应用灵活组合。本文将深入探讨微前端架构中微内核思想的应用,以及如何构建高效的插件化微前端系统。
一、微前端架构设计原理
1.1 微内核架构模式
微前端的微内核架构将整个系统分为两个核心部分:
- 主应用内核(Main Shell): 负责应用注册、路由管理、通信协调和资源调度
- 微应用插件(Micro Apps): 独立开发和部署的业务模块,可动态加载和卸载
1.2 微前端微内核架构图
graph TD
subgraph "微前端微内核架构"
A[主应用 Shell
应用内核] --> B[应用注册中心
Registry] A --> C[路由管理器
Router Manager] A --> D[通信总线
Event Bus] A --> E[资源加载器
Loader] B --> F[应用清单
Manifest] C --> G[路由表
Routes] D --> H[消息队列
Message Queue] E --> I[模块缓存
Module Cache] subgraph "微应用生态" J[用户中心
User App] K[订单系统
Order App] L[商品管理
Product App] M[支付服务
Payment App] end A --> J A --> K A --> L A --> M subgraph "基础服务层" N[沙箱隔离
Sandbox] O[状态管理
State Manager] P[样式隔离
Style Isolation] Q[错误边界
Error Boundary] end J --> N K --> N L --> N M --> N A --> O A --> P A --> Q end style A fill:#FF6B6B style B fill:#4ECDC4 style C fill:#45B7D1 style D fill:#96CEB4
应用内核] --> B[应用注册中心
Registry] A --> C[路由管理器
Router Manager] A --> D[通信总线
Event Bus] A --> E[资源加载器
Loader] B --> F[应用清单
Manifest] C --> G[路由表
Routes] D --> H[消息队列
Message Queue] E --> I[模块缓存
Module Cache] subgraph "微应用生态" J[用户中心
User App] K[订单系统
Order App] L[商品管理
Product App] M[支付服务
Payment App] end A --> J A --> K A --> L A --> M subgraph "基础服务层" N[沙箱隔离
Sandbox] O[状态管理
State Manager] P[样式隔离
Style Isolation] Q[错误边界
Error Boundary] end J --> N K --> N L --> N M --> N A --> O A --> P A --> Q end style A fill:#FF6B6B style B fill:#4ECDC4 style C fill:#45B7D1 style D fill:#96CEB4
1.3 主应用内核实现
主应用内核是整个微前端系统的控制中心,负责管理微应用的生命周期:
javascript
// 微前端主应用内核
class MicroFrontendKernel {
constructor() {
this.apps = new Map()
this.router = new RouterManager()
this.eventBus = new EventBus()
this.loader = new ModuleLoader()
this.registry = new AppRegistry()
this.sandbox = new SandboxManager()
this.initializeKernel()
}
// 初始化内核
initializeKernel() {
// 设置全局错误处理
this.setupGlobalErrorHandling()
// 初始化路由系统
this.router.initialize()
// 启动应用发现
this.startAppDiscovery()
// 监听路由变化
this.listenToRouteChanges()
}
// 注册微应用
registerApp(appConfig) {
const {
name,
entry,
container,
activeWhen,
props = {}
} = appConfig
// 验证应用配置
this.validateAppConfig(appConfig)
// 创建应用实例
const app = new MicroApp({
name,
entry,
container,
activeWhen,
props,
kernel: this
})
// 注册到应用清单
this.apps.set(name, app)
this.registry.register(name, app)
// 设置路由规则
this.router.addRoute(activeWhen, () => this.activateApp(name))
return app
}
// 激活微应用
async activateApp(appName) {
const app = this.apps.get(appName)
if (!app) {
throw new Error(`Application ${appName} not found`)
}
try {
// 检查应用状态
if (app.status === 'ACTIVE') {
return app
}
// 创建沙箱环境
const sandbox = await this.sandbox.createSandbox(appName)
// 加载应用资源
const appModule = await this.loader.loadApp(app.entry)
// 挂载应用
await this.mountApp(app, appModule, sandbox)
// 更新应用状态
app.status = 'ACTIVE'
// 触发应用激活事件
this.eventBus.emit('app:activated', { name: appName, app })
return app
} catch (error) {
this.handleAppError(appName, error)
throw error
}
}
// 挂载微应用
async mountApp(app, appModule, sandbox) {
const container = document.querySelector(app.container)
if (!container) {
throw new Error(`Container ${app.container} not found`)
}
// 在沙箱中执行应用
const result = await sandbox.exec(() => {
return appModule.mount({
container,
props: app.props,
router: this.router.createSubRouter(app.activeWhen),
eventBus: this.eventBus.createNamespace(app.name)
})
})
app.instance = result
app.sandbox = sandbox
app.container = container
}
// 卸载微应用
async unmountApp(appName) {
const app = this.apps.get(appName)
if (!app || app.status !== 'ACTIVE') {
return
}
try {
// 调用应用卸载方法
if (app.instance && app.instance.unmount) {
await app.instance.unmount()
}
// 清理DOM
if (app.container) {
app.container.innerHTML = ''
}
// 销毁沙箱
if (app.sandbox) {
await app.sandbox.destroy()
}
// 更新应用状态
app.status = 'INACTIVE'
app.instance = null
app.sandbox = null
// 触发卸载事件
this.eventBus.emit('app:unmounted', { name: appName, app })
} catch (error) {
this.handleAppError(appName, error)
}
}
// 应用配置验证
validateAppConfig(config) {
const required = ['name', 'entry', 'container', 'activeWhen']
required.forEach(field => {
if (!config[field]) {
throw new Error(`Application config missing required field: ${field}`)
}
})
// 检查应用名称唯一性
if (this.apps.has(config.name)) {
throw new Error(`Application ${config.name} already registered`)
}
}
// 监听路由变化
listenToRouteChanges() {
this.router.onRouteChange(async (newRoute, oldRoute) => {
// 卸载不匹配的应用
for (const [name, app] of this.apps) {
if (app.status === 'ACTIVE' && !this.router.isRouteActive(app.activeWhen)) {
await this.unmountApp(name)
}
}
// 激活匹配的应用
for (const [name, app] of this.apps) {
if (app.status !== 'ACTIVE' && this.router.isRouteActive(app.activeWhen)) {
await this.activateApp(name)
}
}
})
}
// 全局错误处理
setupGlobalErrorHandling() {
window.addEventListener('error', (event) => {
this.handleGlobalError(event.error)
})
window.addEventListener('unhandledrejection', (event) => {
this.handleGlobalError(event.reason)
})
}
// 处理应用错误
handleAppError(appName, error) {
console.error(`Error in micro app ${appName}:`, error)
this.eventBus.emit('app:error', {
name: appName,
error,
timestamp: Date.now()
})
}
// 处理全局错误
handleGlobalError(error) {
console.error('Global error:', error)
this.eventBus.emit('global:error', {
error,
timestamp: Date.now()
})
}
// 启动应用发现
startAppDiscovery() {
// 可以实现自动发现机制
// 比如从配置服务器获取应用列表
this.loadAppsFromConfig()
}
// 从配置加载应用
async loadAppsFromConfig() {
try {
// 模拟从配置服务获取应用列表
const appConfigs = await this.fetchAppConfigs()
appConfigs.forEach(config => {
this.registerApp(config)
})
} catch (error) {
console.error('Failed to load app configs:', error)
}
}
// 获取应用配置
async fetchAppConfigs() {
// 实际实现中可能从远程配置服务获取
return [
{
name: 'user-center',
entry: '/micro-apps/user/index.js',
container: '#user-container',
activeWhen: '/user'
},
{
name: 'order-system',
entry: '/micro-apps/order/index.js',
container: '#order-container',
activeWhen: '/order'
}
]
}
}
二、微前端插件系统实现
2.1 模块联邦机制
模块联邦是微前端插件化的核心技术,实现了运行时的模块共享:
javascript
// 模块联邦配置
class ModuleFederationManager {
constructor() {
this.remoteModules = new Map()
this.sharedModules = new Map()
this.moduleCache = new Map()
this.loadingPromises = new Map()
}
// 注册远程模块
registerRemoteModule(name, config) {
const {
url,
scope,
module,
version = 'latest'
} = config
this.remoteModules.set(name, {
url,
scope,
module,
version,
loaded: false,
exports: null
})
}
// 动态导入远程模块
async importRemoteModule(name) {
const moduleConfig = this.remoteModules.get(name)
if (!moduleConfig) {
throw new Error(`Remote module ${name} not found`)
}
// 检查缓存
const cacheKey = `${name}@${moduleConfig.version}`
if (this.moduleCache.has(cacheKey)) {
return this.moduleCache.get(cacheKey)
}
// 检查是否正在加载
if (this.loadingPromises.has(name)) {
return this.loadingPromises.get(name)
}
// 开始加载模块
const loadingPromise = this.loadRemoteModule(moduleConfig)
this.loadingPromises.set(name, loadingPromise)
try {
const moduleExports = await loadingPromise
this.moduleCache.set(cacheKey, moduleExports)
this.loadingPromises.delete(name)
return moduleExports
} catch (error) {
this.loadingPromises.delete(name)
throw error
}
}
// 加载远程模块实现
async loadRemoteModule(config) {
const { url, scope, module } = config
// 动态加载远程入口文件
await this.loadScript(url)
// 获取远程容器
const container = window[scope]
if (!container) {
throw new Error(`Remote container ${scope} not found`)
}
// 初始化容器
await container.init(__webpack_share_scopes__.default)
// 获取模块工厂
const factory = await container.get(module)
// 执行工厂函数获取模块
const moduleExports = factory()
return moduleExports
}
// 动态加载脚本
loadScript(url) {
return new Promise((resolve, reject) => {
const existingScript = document.querySelector(`script[src="${url}"]`)
if (existingScript) {
resolve()
return
}
const script = document.createElement('script')
script.src = url
script.type = 'text/javascript'
script.onload = () => resolve()
script.onerror = () => reject(new Error(`Failed to load script: ${url}`))
document.head.appendChild(script)
})
}
// 共享模块管理
shareModule(name, module, version = '1.0.0') {
this.sharedModules.set(name, {
module,
version,
shareScope: 'default'
})
}
// 获取共享模块
getSharedModule(name) {
return this.sharedModules.get(name)
}
// 创建模块联邦配置
createWebpackConfig(appName, remotes = {}, exposes = {}) {
return {
plugins: [
new ModuleFederationPlugin({
name: appName,
remotes,
exposes,
shared: {
react: {
singleton: true,
requiredVersion: '^17.0.0'
},
'react-dom': {
singleton: true,
requiredVersion: '^17.0.0'
},
'react-router-dom': {
singleton: true,
requiredVersion: '^6.0.0'
}
}
})
]
}
}
}
2.2 插件生命周期管理
javascript
// 微前端插件生命周期管理
class MicroAppPlugin {
constructor(config) {
this.name = config.name
this.version = config.version || '1.0.0'
this.dependencies = config.dependencies || []
this.config = config
this.status = 'UNLOADED'
this.instance = null
this.hooks = new Map()
}
// 插件生命周期钩子
static LIFECYCLE_HOOKS = {
BEFORE_LOAD: 'beforeLoad',
LOADED: 'loaded',
BEFORE_MOUNT: 'beforeMount',
MOUNTED: 'mounted',
BEFORE_UNMOUNT: 'beforeUnmount',
UNMOUNTED: 'unmounted',
ERROR: 'error'
}
// 注册生命周期钩子
registerHook(hookName, callback) {
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, [])
}
this.hooks.get(hookName).push(callback)
}
// 执行生命周期钩子
async executeHook(hookName, ...args) {
const callbacks = this.hooks.get(hookName) || []
for (const callback of callbacks) {
try {
await callback(...args)
} catch (error) {
console.error(`Hook ${hookName} execution failed:`, error)
this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.ERROR, error)
}
}
}
// 加载插件
async load(kernel) {
if (this.status !== 'UNLOADED') {
return
}
try {
// 执行加载前钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.BEFORE_LOAD, kernel)
// 检查依赖
await this.checkDependencies(kernel)
// 加载插件资源
const pluginModule = await this.loadResources()
// 创建插件实例
this.instance = await this.createInstance(pluginModule, kernel)
// 更新状态
this.status = 'LOADED'
// 执行加载完成钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.LOADED, this.instance)
} catch (error) {
this.status = 'ERROR'
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.ERROR, error)
throw error
}
}
// 挂载插件
async mount(container, props = {}) {
if (this.status !== 'LOADED') {
throw new Error(`Plugin ${this.name} must be loaded before mounting`)
}
try {
// 执行挂载前钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.BEFORE_MOUNT, container, props)
// 挂载插件
if (this.instance && this.instance.mount) {
await this.instance.mount(container, props)
}
// 更新状态
this.status = 'MOUNTED'
// 执行挂载完成钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.MOUNTED, container)
} catch (error) {
this.status = 'ERROR'
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.ERROR, error)
throw error
}
}
// 卸载插件
async unmount() {
if (this.status !== 'MOUNTED') {
return
}
try {
// 执行卸载前钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.BEFORE_UNMOUNT)
// 卸载插件
if (this.instance && this.instance.unmount) {
await this.instance.unmount()
}
// 更新状态
this.status = 'LOADED'
// 执行卸载完成钩子
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.UNMOUNTED)
} catch (error) {
await this.executeHook(MicroAppPlugin.LIFECYCLE_HOOKS.ERROR, error)
throw error
}
}
// 检查依赖
async checkDependencies(kernel) {
for (const dependency of this.dependencies) {
const depPlugin = kernel.getPlugin(dependency)
if (!depPlugin || depPlugin.status !== 'LOADED') {
throw new Error(`Dependency ${dependency} not available`)
}
}
}
// 加载插件资源
async loadResources() {
const { entry } = this.config
if (typeof entry === 'string') {
// 加载远程资源
return await this.loadRemoteResource(entry)
} else if (typeof entry === 'function') {
// 直接使用函数
return entry
} else {
throw new Error('Invalid plugin entry configuration')
}
}
// 加载远程资源
async loadRemoteResource(url) {
// 动态导入
const module = await import(url)
return module.default || module
}
// 创建插件实例
async createInstance(pluginModule, kernel) {
if (typeof pluginModule === 'function') {
return new pluginModule(this.config, kernel)
} else if (pluginModule.default && typeof pluginModule.default === 'function') {
return new pluginModule.default(this.config, kernel)
} else {
return pluginModule
}
}
// 获取插件信息
getInfo() {
return {
name: this.name,
version: this.version,
status: this.status,
dependencies: this.dependencies
}
}
}
三、应用间通信机制
3.1 事件总线实现
微前端应用间通信的核心是事件总线系统:
javascript
// 微前端事件总线
class MicroFrontendEventBus {
constructor() {
this.events = new Map()
this.namespaces = new Map()
this.middleware = []
this.globalFilters = []
}
// 创建命名空间
createNamespace(namespace) {
if (this.namespaces.has(namespace)) {
return this.namespaces.get(namespace)
}
const namespacedBus = new NamespacedEventBus(this, namespace)
this.namespaces.set(namespace, namespacedBus)
return namespacedBus
}
// 注册事件监听器
on(eventName, callback, options = {}) {
const {
namespace = 'global',
once = false,
priority = 0
} = options
const fullEventName = `${namespace}:${eventName}`
if (!this.events.has(fullEventName)) {
this.events.set(fullEventName, [])
}
const listener = {
callback,
once,
priority,
namespace,
id: this.generateListenerId()
}
// 按优先级插入
const listeners = this.events.get(fullEventName)
const insertIndex = listeners.findIndex(l => l.priority < priority)
if (insertIndex === -1) {
listeners.push(listener)
} else {
listeners.splice(insertIndex, 0, listener)
}
return listener.id
}
// 移除事件监听器
off(eventName, listenerId, namespace = 'global') {
const fullEventName = `${namespace}:${eventName}`
const listeners = this.events.get(fullEventName)
if (!listeners) return false
const index = listeners.findIndex(l => l.id === listenerId)
if (index !== -1) {
listeners.splice(index, 1)
return true
}
return false
}
// 触发事件
async emit(eventName, data, options = {}) {
const {
namespace = 'global',
sync = false,
timeout = 5000
} = options
const fullEventName = `${namespace}:${eventName}`
const listeners = this.events.get(fullEventName) || []
// 应用全局过滤器
if (!this.applyGlobalFilters(eventName, data, namespace)) {
return
}
// 应用中间件
const processedData = await this.applyMiddleware(eventName, data, namespace)
const eventContext = {
eventName: fullEventName,
data: processedData,
namespace,
timestamp: Date.now(),
source: namespace
}
if (sync) {
// 同步执行
for (const listener of listeners) {
await this.executeListener(listener, eventContext)
}
} else {
// 异步执行
const promises = listeners.map(listener =>
this.executeListener(listener, eventContext, timeout)
)
await Promise.allSettled(promises)
}
}
// 执行监听器
async executeListener(listener, eventContext, timeout) {
try {
const promise = listener.callback(eventContext.data, eventContext)
if (timeout) {
await Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Listener timeout')), timeout)
)
])
} else {
await promise
}
// 如果是一次性监听器,移除它
if (listener.once) {
this.removeListener(listener, eventContext.eventName)
}
} catch (error) {
console.error('Event listener execution failed:', error)
this.emit('error', {
listener: listener.id,
event: eventContext.eventName,
error: error.message
})
}
}
// 添加中间件
use(middleware) {
this.middleware.push(middleware)
}
// 应用中间件
async applyMiddleware(eventName, data, namespace) {
let processedData = data
for (const middleware of this.middleware) {
try {
processedData = await middleware(eventName, processedData, namespace)
} catch (error) {
console.error('Middleware execution failed:', error)
}
}
return processedData
}
// 添加全局过滤器
addGlobalFilter(filter) {
this.globalFilters.push(filter)
}
// 应用全局过滤器
applyGlobalFilters(eventName, data, namespace) {
return this.globalFilters.every(filter =>
filter(eventName, data, namespace)
)
}
// 生成监听器ID
generateListenerId() {
return `listener_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
}
// 移除监听器
removeListener(listener, eventName) {
const listeners = this.events.get(eventName)
if (listeners) {
const index = listeners.findIndex(l => l.id === listener.id)
if (index !== -1) {
listeners.splice(index, 1)
}
}
}
// 清理命名空间
clearNamespace(namespace) {
for (const [eventName, listeners] of this.events) {
if (eventName.startsWith(`${namespace}:`)) {
this.events.delete(eventName)
}
}
this.namespaces.delete(namespace)
}
// 获取统计信息
getStats() {
const stats = {
totalEvents: this.events.size,
totalListeners: 0,
namespaces: Array.from(this.namespaces.keys()),
middleware: this.middleware.length
}
for (const listeners of this.events.values()) {
stats.totalListeners += listeners.length
}
return stats
}
}
// 命名空间事件总线
class NamespacedEventBus {
constructor(globalBus, namespace) {
this.globalBus = globalBus
this.namespace = namespace
}
on(eventName, callback, options = {}) {
return this.globalBus.on(eventName, callback, {
...options,
namespace: this.namespace
})
}
off(eventName, listenerId) {
return this.globalBus.off(eventName, listenerId, this.namespace)
}
emit(eventName, data, options = {}) {
return this.globalBus.emit(eventName, data, {
...options,
namespace: this.namespace
})
}
// 广播到所有命名空间
broadcast(eventName, data) {
return this.globalBus.emit(eventName, data, {
namespace: 'global'
})
}
}
3.2 路由管理系统
javascript
// 微前端路由管理器
class MicroFrontendRouter {
constructor() {
this.routes = new Map()
this.subrouters = new Map()
this.currentRoute = null
this.listeners = []
this.baseUrl = ''
this.mode = 'history' // 'history' or 'hash'
this.initializeRouter()
}
// 初始化路由器
initializeRouter() {
// 监听浏览器历史变化
window.addEventListener('popstate', (event) => {
this.handleRouteChange()
})
// 拦截链接点击
this.interceptLinkClicks()
// 初始化当前路由
this.handleRouteChange()
}
// 添加路由规则
addRoute(pattern, handler, options = {}) {
const {
exact = false,
priority = 0,
guard = null
} = options
const route = {
pattern: this.normalizePattern(pattern),
handler,
exact,
priority,
guard,
regex: this.patternToRegex(pattern)
}
this.routes.set(pattern, route)
// 按优先级排序路由
this.sortRoutes()
return route
}
// 移除路由
removeRoute(pattern) {
return this.routes.delete(pattern)
}
// 导航到指定路由
push(path, state = {}) {
const url = this.createURL(path)
if (this.mode === 'history') {
history.pushState(state, '', url)
} else {
window.location.hash = path
}
this.handleRouteChange()
}
// 替换当前路由
replace(path, state = {}) {
const url = this.createURL(path)
if (this.mode === 'history') {
history.replaceState(state, '', url)
} else {
window.location.hash = path
}
this.handleRouteChange()
}
// 后退
back() {
history.back()
}
// 前进
forward() {
history.forward()
}
// 处理路由变化
async handleRouteChange() {
const currentPath = this.getCurrentPath()
const matchedRoute = this.matchRoute(currentPath)
// 路由守卫检查
if (matchedRoute && matchedRoute.guard) {
const guardResult = await matchedRoute.guard(currentPath, this.currentRoute)
if (guardResult === false) {
return
}
if (typeof guardResult === 'string') {
this.push(guardResult)
return
}
}
const oldRoute = this.currentRoute
this.currentRoute = {
path: currentPath,
route: matchedRoute,
params: matchedRoute ? this.extractParams(matchedRoute, currentPath) : {},
query: this.parseQuery(),
timestamp: Date.now()
}
// 通知路由变化
this.notifyRouteChange(this.currentRoute, oldRoute)
// 执行路由处理器
if (matchedRoute) {
try {
await matchedRoute.handler(this.currentRoute)
} catch (error) {
console.error('Route handler execution failed:', error)
this.handleRouteError(error, this.currentRoute)
}
}
}
// 匹配路由
matchRoute(path) {
for (const route of this.routes.values()) {
if (route.exact) {
if (route.pattern === path) {
return route
}
} else {
if (route.regex.test(path)) {
return route
}
}
}
return null
}
// 创建子路由器
createSubRouter(basePath) {
const subrouter = new SubRouter(this, basePath)
this.subrouters.set(basePath, subrouter)
return subrouter
}
// 检查路由是否激活
isRouteActive(pattern) {
const currentPath = this.getCurrentPath()
if (typeof pattern === 'string') {
return currentPath.startsWith(pattern)
} else if (pattern instanceof RegExp) {
return pattern.test(currentPath)
} else if (typeof pattern === 'function') {
return pattern(currentPath)
}
return false
}
// 监听路由变化
onRouteChange(listener) {
this.listeners.push(listener)
// 返回取消监听的函数
return () => {
const index = this.listeners.indexOf(listener)
if (index !== -1) {
this.listeners.splice(index, 1)
}
}
}
// 通知路由变化
notifyRouteChange(newRoute, oldRoute) {
this.listeners.forEach(listener => {
try {
listener(newRoute, oldRoute)
} catch (error) {
console.error('Route change listener failed:', error)
}
})
}
// 获取当前路径
getCurrentPath() {
if (this.mode === 'history') {
return window.location.pathname + window.location.search
} else {
return window.location.hash.slice(1) || '/'
}
}
// 解析查询参数
parseQuery() {
const search = window.location.search
const query = {}
if (search) {
const params = new URLSearchParams(search)
for (const [key, value] of params) {
query[key] = value
}
}
return query
}
// 提取路由参数
extractParams(route, path) {
const params = {}
const matches = path.match(route.regex)
if (matches) {
const paramNames = route.pattern.match(/:(\w+)/g) || []
paramNames.forEach((param, index) => {
const paramName = param.slice(1)
params[paramName] = matches[index + 1]
})
}
return params
}
// 模式转正则
patternToRegex(pattern) {
const regexPattern = pattern
.replace(/:[^/]+/g, '([^/]+)')
.replace(/\*/g, '(.*)')
return new RegExp(`^${regexPattern}$`)
}
// 标准化模式
normalizePattern(pattern) {
return pattern.startsWith('/') ? pattern : `/${pattern}`
}
// 创建URL
createURL(path) {
return this.baseUrl + this.normalizePattern(path)
}
// 拦截链接点击
interceptLinkClicks() {
document.addEventListener('click', (event) => {
const link = event.target.closest('a')
if (link &&
link.origin === window.location.origin &&
!link.hasAttribute('target') &&
!event.ctrlKey &&
!event.metaKey) {
event.preventDefault()
this.push(link.pathname + link.search)
}
})
}
// 路由排序
sortRoutes() {
const routes = Array.from(this.routes.values())
routes.sort((a, b) => b.priority - a.priority)
this.routes.clear()
routes.forEach(route => {
this.routes.set(route.pattern, route)
})
}
// 处理路由错误
handleRouteError(error, route) {
console.error(`Route error for ${route.path}:`, error)
// 可以触发错误页面或回退逻辑
this.push('/error')
}
}
// 子路由器
class SubRouter {
constructor(parentRouter, basePath) {
this.parent = parentRouter
this.basePath = basePath.replace(/\/$/, '') // 移除末尾斜杠
}
push(path) {
const fullPath = this.basePath + path
this.parent.push(fullPath)
}
replace(path) {
const fullPath = this.basePath + path
this.parent.replace(fullPath)
}
getCurrentPath() {
const fullPath = this.parent.getCurrentPath()
return fullPath.startsWith(this.basePath)
? fullPath.slice(this.basePath.length) || '/'
: '/'
}
}
四、微前端应用加载流程
4.1 应用加载流程图
sequenceDiagram
participant User as 用户
participant Shell as 主应用Shell
participant Router as 路由管理器
participant Registry as 应用注册中心
participant Loader as 模块加载器
participant Sandbox as 沙箱管理器
participant MicroApp as 微应用
User->>Shell: 访问路由 /user
Shell->>Router: 路由变化通知
Router->>Registry: 查找匹配应用
Registry->>Router: 返回应用配置
Router->>Shell: 激活应用请求
Shell->>Loader: 加载应用资源
Loader->>Loader: 检查模块缓存
alt 缓存未命中
Loader->>MicroApp: 下载应用bundle
MicroApp->>Loader: 返回模块代码
Loader->>Loader: 缓存模块
end
Shell->>Sandbox: 创建隔离环境
Sandbox->>Shell: 返回沙箱实例
Shell->>MicroApp: 在沙箱中实例化
MicroApp->>Shell: 返回应用实例
Shell->>MicroApp: 挂载到DOM容器
MicroApp->>User: 渲染用户界面
note over User,MicroApp: 应用成功加载和渲染
4.2 模块加载器实现
javascript
// 微前端模块加载器
class ModuleLoader {
constructor() {
this.cache = new Map()
this.loadingPromises = new Map()
this.retryConfig = {
maxRetries: 3,
retryDelay: 1000
}
this.cdnFallbacks = []
}
// 加载应用模块
async loadApp(entry, options = {}) {
const {
timeout = 30000,
cache = true,
fallback = true
} = options
// 生成缓存键
const cacheKey = this.generateCacheKey(entry, options)
// 检查缓存
if (cache && this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)
}
// 检查是否正在加载
if (this.loadingPromises.has(cacheKey)) {
return this.loadingPromises.get(cacheKey)
}
// 开始加载
const loadingPromise = this.loadModuleWithFallback(entry, options)
this.loadingPromises.set(cacheKey, loadingPromise)
try {
const module = await Promise.race([
loadingPromise,
this.createTimeout(timeout)
])
// 缓存结果
if (cache) {
this.cache.set(cacheKey, module)
}
this.loadingPromises.delete(cacheKey)
return module
} catch (error) {
this.loadingPromises.delete(cacheKey)
throw error
}
}
// 带降级的模块加载
async loadModuleWithFallback(entry, options) {
const urls = this.buildUrlList(entry, options.fallback)
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
try {
return await this.loadSingleModule(url, options)
} catch (error) {
console.warn(`Failed to load from ${url}:`, error)
// 如果不是最后一个URL,继续尝试下一个
if (i < urls.length - 1) {
continue
}
throw new Error(`Failed to load module from all sources: ${entry}`)
}
}
}
// 加载单个模块
async loadSingleModule(url, options = {}) {
const { retries = this.retryConfig.maxRetries } = options
for (let attempt = 0; attempt <= retries; attempt++) {
try {
// 检查资源类型
if (url.endsWith('.js')) {
return await this.loadJavaScriptModule(url)
} else if (url.endsWith('.css')) {
return await this.loadStylesheet(url)
} else {
// 尝试作为JavaScript模块加载
return await this.loadJavaScriptModule(url)
}
} catch (error) {
if (attempt < retries) {
// 等待后重试
await this.delay(this.retryConfig.retryDelay * Math.pow(2, attempt))
continue
}
throw error
}
}
}
// 加载JavaScript模块
async loadJavaScriptModule(url) {
// 优先使用动态导入
if (this.supportsDynamicImport()) {
try {
const module = await import(url)
return this.normalizeModule(module)
} catch (error) {
// 降级到脚本加载
console.warn('Dynamic import failed, falling back to script loading')
}
}
// 脚本标签加载
return this.loadScriptTag(url)
}
// 脚本标签加载
loadScriptTag(url) {
return new Promise((resolve, reject) => {
// 检查是否已存在
const existingScript = document.querySelector(`script[src="${url}"]`)
if (existingScript) {
// 如果脚本已加载,尝试获取模块
const module = this.extractModuleFromGlobal(url)
if (module) {
resolve(module)
return
}
}
const script = document.createElement('script')
script.src = url
script.type = 'text/javascript'
script.crossOrigin = 'anonymous'
script.onload = () => {
try {
const module = this.extractModuleFromGlobal(url)
resolve(module)
} catch (error) {
reject(error)
}
}
script.onerror = () => {
document.head.removeChild(script)
reject(new Error(`Failed to load script: ${url}`))
}
document.head.appendChild(script)
})
}
// 加载样式表
loadStylesheet(url) {
return new Promise((resolve, reject) => {
// 检查是否已存在
const existingLink = document.querySelector(`link[href="${url}"]`)
if (existingLink) {
resolve({ type: 'stylesheet', url })
return
}
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
link.crossOrigin = 'anonymous'
link.onload = () => {
resolve({ type: 'stylesheet', url })
}
link.onerror = () => {
document.head.removeChild(link)
reject(new Error(`Failed to load stylesheet: ${url}`))
}
document.head.appendChild(link)
})
}
// 从全局对象提取模块
extractModuleFromGlobal(url) {
// 这里需要根据具体的模块约定来实现
// 例如UMD模块可能会暴露到window对象上
const moduleName = this.extractModuleNameFromUrl(url)
if (window[moduleName]) {
return this.normalizeModule(window[moduleName])
}
throw new Error(`Module not found in global scope: ${moduleName}`)
}
// 从URL提取模块名
extractModuleNameFromUrl(url) {
const parts = url.split('/')
const filename = parts[parts.length - 1]
return filename.replace(/\.(js|css)$/, '').replace(/[^a-zA-Z0-9]/g, '_')
}
// 标准化模块
normalizeModule(module) {
// 处理不同的模块格式
if (module.default) {
return module.default
}
if (typeof module === 'function') {
return module
}
if (module.mount || module.unmount) {
return module
}
return module
}
// 构建URL列表
buildUrlList(entry, enableFallback = true) {
const urls = [entry]
if (enableFallback) {
// 添加CDN降级地址
this.cdnFallbacks.forEach(cdn => {
if (entry.startsWith('http')) {
const path = new URL(entry).pathname
urls.push(cdn + path)
}
})
}
return urls
}
// 检查动态导入支持
supportsDynamicImport() {
try {
new Function('import("")')
return true
} catch {
return false
}
}
// 生成缓存键
generateCacheKey(entry, options) {
return `${entry}:${JSON.stringify(options)}`
}
// 创建超时Promise
createTimeout(timeout) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Module loading timeout: ${timeout}ms`))
}, timeout)
})
}
// 延迟函数
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// 添加CDN降级
addCDNFallback(cdnUrl) {
this.cdnFallbacks.push(cdnUrl)
}
// 清除缓存
clearCache(pattern) {
if (pattern) {
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key)
}
}
} else {
this.cache.clear()
}
}
// 获取缓存统计
getCacheStats() {
return {
size: this.cache.size,
loading: this.loadingPromises.size,
fallbacks: this.cdnFallbacks.length
}
}
}
五、沙箱隔离与状态管理
5.1 JavaScript沙箱实现
javascript
// JavaScript沙箱管理器
class SandboxManager {
constructor() {
this.sandboxes = new Map()
this.globalBackup = null
}
// 创建沙箱
async createSandbox(appName, options = {}) {
const {
strictMode = true,
allowedGlobals = ['console', 'setTimeout', 'setInterval'],
deniedGlobals = ['eval', 'Function'],
isolateStorage = true
} = options
if (this.sandboxes.has(appName)) {
return this.sandboxes.get(appName)
}
const sandbox = new JavaScriptSandbox(appName, {
strictMode,
allowedGlobals,
deniedGlobals,
isolateStorage
})
await sandbox.initialize()
this.sandboxes.set(appName, sandbox)
return sandbox
}
// 销毁沙箱
async destroySandbox(appName) {
const sandbox = this.sandboxes.get(appName)
if (sandbox) {
await sandbox.destroy()
this.sandboxes.delete(appName)
}
}
// 获取沙箱
getSandbox(appName) {
return this.sandboxes.get(appName)
}
// 清理所有沙箱
async cleanup() {
for (const [name, sandbox] of this.sandboxes) {
await sandbox.destroy()
}
this.sandboxes.clear()
}
}
// JavaScript沙箱实现
class JavaScriptSandbox {
constructor(name, options = {}) {
this.name = name
this.options = options
this.proxyWindow = null
this.originalValues = new Map()
this.modifiedKeys = new Set()
this.active = false
}
// 初始化沙箱
async initialize() {
// 创建代理窗口对象
this.proxyWindow = this.createProxyWindow()
// 设置存储隔离
if (this.options.isolateStorage) {
this.setupStorageIsolation()
}
// 注入允许的全局对象
this.injectAllowedGlobals()
}
// 创建代理窗口
createProxyWindow() {
const sandbox = this
const originalWindow = window
const fakeWindow = {}
return new Proxy(fakeWindow, {
get(target, key) {
// 检查是否为被拒绝的全局变量
if (sandbox.options.deniedGlobals.includes(key)) {
throw new Error(`Access to ${key} is denied in sandbox`)
}
// 优先从沙箱环境获取
if (key in target) {
return target[key]
}
// 检查是否为允许的全局变量
if (sandbox.options.allowedGlobals.includes(key)) {
return originalWindow[key]
}
// 特殊处理window对象
if (key === 'window' || key === 'self' || key === 'globalThis') {
return sandbox.proxyWindow
}
// 返回原始窗口的值
return originalWindow[key]
},
set(target, key, value) {
// 记录修改的键
sandbox.modifiedKeys.add(key)
// 备份原始值
if (!sandbox.originalValues.has(key) && key in originalWindow) {
sandbox.originalValues.set(key, originalWindow[key])
}
// 设置到沙箱环境
target[key] = value
return true
},
has(target, key) {
return key in target || key in originalWindow
},
deleteProperty(target, key) {
sandbox.modifiedKeys.add(key)
delete target[key]
return true
}
})
}
// 设置存储隔离
setupStorageIsolation() {
const sandboxStorage = this.createSandboxStorage()
this.proxyWindow.localStorage = sandboxStorage.localStorage
this.proxyWindow.sessionStorage = sandboxStorage.sessionStorage
}
// 创建沙箱存储
createSandboxStorage() {
const createStorage = (originalStorage) => {
const storage = {}
const prefix = `sandbox_${this.name}_`
return {
getItem(key) {
return originalStorage.getItem(prefix + key)
},
setItem(key, value) {
originalStorage.setItem(prefix + key, value)
},
removeItem(key) {
originalStorage.removeItem(prefix + key)
},
clear() {
// 只清除当前沙箱的数据
for (let i = originalStorage.length - 1; i >= 0; i--) {
const key = originalStorage.key(i)
if (key && key.startsWith(prefix)) {
originalStorage.removeItem(key)
}
}
},
key(index) {
const keys = []
for (let i = 0; i < originalStorage.length; i++) {
const key = originalStorage.key(i)
if (key && key.startsWith(prefix)) {
keys.push(key.slice(prefix.length))
}
}
return keys[index] || null
},
get length() {
let count = 0
for (let i = 0; i < originalStorage.length; i++) {
const key = originalStorage.key(i)
if (key && key.startsWith(prefix)) {
count++
}
}
return count
}
}
}
return {
localStorage: createStorage(window.localStorage),
sessionStorage: createStorage(window.sessionStorage)
}
}
// 注入允许的全局对象
injectAllowedGlobals() {
this.options.allowedGlobals.forEach(globalName => {
if (window[globalName]) {
this.proxyWindow[globalName] = window[globalName]
}
})
}
// 激活沙箱
activate() {
if (this.active) return
// 备份当前全局变量状态
this.backupGlobals()
// 应用沙箱修改
this.applyModifications()
this.active = true
}
// 停用沙箱
deactivate() {
if (!this.active) return
// 恢复全局变量状态
this.restoreGlobals()
this.active = false
}
// 在沙箱中执行代码
async exec(code) {
this.activate()
try {
let result
if (typeof code === 'function') {
// 执行函数
result = await code.call(this.proxyWindow)
} else if (typeof code === 'string') {
// 执行代码字符串
const func = new Function('window', `
with (window) {
${this.options.strictMode ? '"use strict";' : ''}
return (${code});
}
`)
result = await func.call(this.proxyWindow, this.proxyWindow)
} else {
throw new Error('Invalid code type')
}
return result
} finally {
this.deactivate()
}
}
// 备份全局变量
backupGlobals() {
this.modifiedKeys.forEach(key => {
if (!this.originalValues.has(key) && key in window) {
this.originalValues.set(key, window[key])
}
})
}
// 应用修改
applyModifications() {
this.modifiedKeys.forEach(key => {
if (key in this.proxyWindow) {
window[key] = this.proxyWindow[key]
}
})
}
// 恢复全局变量
restoreGlobals() {
this.modifiedKeys.forEach(key => {
if (this.originalValues.has(key)) {
window[key] = this.originalValues.get(key)
} else if (key in window) {
delete window[key]
}
})
}
// 销毁沙箱
async destroy() {
if (this.active) {
this.deactivate()
}
// 清理存储
if (this.options.isolateStorage) {
this.proxyWindow.localStorage?.clear()
this.proxyWindow.sessionStorage?.clear()
}
// 清理引用
this.proxyWindow = null
this.originalValues.clear()
this.modifiedKeys.clear()
}
// 获取沙箱状态
getStatus() {
return {
name: this.name,
active: this.active,
modifiedKeys: Array.from(this.modifiedKeys),
originalValuesCount: this.originalValues.size
}
}
}
5.2 样式隔离机制
javascript
// 样式隔离管理器
class StyleIsolationManager {
constructor() {
this.scopedStyles = new Map()
this.shadowRoots = new Map()
this.dynamicStyles = new Map()
}
// 应用样式隔离
applyStyleIsolation(appName, container, mode = 'scoped') {
switch (mode) {
case 'shadow':
return this.createShadowDOMIsolation(appName, container)
case 'scoped':
return this.createScopedStyleIsolation(appName, container)
case 'namespace':
return this.createNamespaceIsolation(appName, container)
default:
throw new Error(`Unknown isolation mode: ${mode}`)
}
}
// Shadow DOM隔离
createShadowDOMIsolation(appName, container) {
if (this.shadowRoots.has(appName)) {
return this.shadowRoots.get(appName)
}
const shadowRoot = container.attachShadow({ mode: 'closed' })
this.shadowRoots.set(appName, shadowRoot)
// 创建样式隔离器
const isolator = {
container: shadowRoot,
// 添加样式
addStyle(css, id) {
const style = document.createElement('style')
style.textContent = css
if (id) style.id = id
shadowRoot.appendChild(style)
return style
},
// 移除样式
removeStyle(id) {
const style = shadowRoot.getElementById(id)
if (style) {
style.remove()
return true
}
return false
},
// 添加外部样式表
addStylesheet(href, id) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
if (id) link.id = id
shadowRoot.appendChild(link)
return link
},
// 清理所有样式
clearStyles() {
const styles = shadowRoot.querySelectorAll('style, link[rel="stylesheet"]')
styles.forEach(style => style.remove())
}
}
return isolator
}
// CSS Scoped隔离
createScopedStyleIsolation(appName, container) {
const scopeId = `micro-app-${appName}`
container.setAttribute('data-micro-app', scopeId)
const isolator = {
container,
scopeId,
// 添加作用域样式
addStyle(css, id) {
const scopedCSS = this.scopeCSS(css, scopeId)
const style = document.createElement('style')
style.textContent = scopedCSS
if (id) style.id = `${scopeId}-${id}`
document.head.appendChild(style)
// 记录样式元素
if (!this.scopedStyles.has(appName)) {
this.scopedStyles.set(appName, new Set())
}
this.scopedStyles.get(appName).add(style)
return style
},
// 移除作用域样式
removeStyle(id) {
const fullId = `${scopeId}-${id}`
const style = document.getElementById(fullId)
if (style) {
style.remove()
const appStyles = this.scopedStyles.get(appName)
if (appStyles) {
appStyles.delete(style)
}
return true
}
return false
},
// 作用域化CSS
scopeCSS(css, scope) {
return css.replace(/([^{}]+){/g, (match, selector) => {
const trimmedSelector = selector.trim()
// 跳过keyframes和media查询
if (trimmedSelector.startsWith('@') ||
trimmedSelector.startsWith('from') ||
trimmedSelector.startsWith('to') ||
/^\d+%/.test(trimmedSelector)) {
return match
}
// 为每个选择器添加作用域
const scopedSelectors = trimmedSelector
.split(',')
.map(s => `[data-micro-app="${scope}"] ${s.trim()}`)
.join(', ')
return `${scopedSelectors} {`
})
},
// 清理样式
clearStyles() {
const appStyles = this.scopedStyles.get(appName)
if (appStyles) {
appStyles.forEach(style => style.remove())
appStyles.clear()
}
}
}
// 绑定作用域化方法
isolator.scopeCSS = isolator.scopeCSS.bind(this)
return isolator
}
// 命名空间隔离
createNamespaceIsolation(appName, container) {
const namespace = `micro-app-${appName}`
container.classList.add(namespace)
return {
container,
namespace,
addStyle(css, id) {
const namespacedCSS = this.namespaceCSS(css, namespace)
const style = document.createElement('style')
style.textContent = namespacedCSS
if (id) style.id = `${namespace}-${id}`
document.head.appendChild(style)
return style
},
namespaceCSS(css, ns) {
return css.replace(/([^{}]+){/g, (match, selector) => {
const selectors = selector.split(',').map(s => {
const trimmed = s.trim()
if (trimmed.startsWith('@') || trimmed === 'html' || trimmed === 'body') {
return trimmed
}
return `.${ns} ${trimmed}`
})
return `${selectors.join(', ')} {`
})
},
clearStyles() {
const styles = document.querySelectorAll(`style[id^="${namespace}-"]`)
styles.forEach(style => style.remove())
}
}
}
// 动态监听样式变化
observeStyleChanges(appName, callback) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE &&
(node.tagName === 'STYLE' ||
(node.tagName === 'LINK' && node.rel === 'stylesheet'))) {
callback({
type: 'added',
element: node,
appName
})
}
})
mutation.removedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE &&
(node.tagName === 'STYLE' ||
(node.tagName === 'LINK' && node.rel === 'stylesheet'))) {
callback({
type: 'removed',
element: node,
appName
})
}
})
})
})
observer.observe(document.head, {
childList: true,
subtree: true
})
return observer
}
// 清理应用样式
cleanupApp(appName) {
// 清理scoped样式
const appStyles = this.scopedStyles.get(appName)
if (appStyles) {
appStyles.forEach(style => style.remove())
this.scopedStyles.delete(appName)
}
// 清理Shadow DOM
const shadowRoot = this.shadowRoots.get(appName)
if (shadowRoot) {
// Shadow DOM会自动清理其内容
this.shadowRoots.delete(appName)
}
// 清理动态样式
const dynamicStyles = this.dynamicStyles.get(appName)
if (dynamicStyles) {
dynamicStyles.forEach(style => style.remove())
this.dynamicStyles.delete(appName)
}
}
// 获取样式统计
getStyleStats() {
return {
scopedApps: this.scopedStyles.size,
shadowApps: this.shadowRoots.size,
dynamicApps: this.dynamicStyles.size,
totalStyles: Array.from(this.scopedStyles.values())
.reduce((total, styles) => total + styles.size, 0)
}
}
}
六、性能优化与治理策略
6.1 预加载与缓存策略
javascript
// 微前端性能优化管理器
class PerformanceOptimizer {
constructor() {
this.preloadQueue = []
this.cacheStrategy = new Map()
this.metrics = new PerformanceMetrics()
this.resourceHints = new Set()
this.bundleAnalyzer = new BundleAnalyzer()
}
// 设置预加载策略
setupPreloadStrategy(apps) {
// 分析应用依赖关系
const dependencyGraph = this.buildDependencyGraph(apps)
// 计算预加载优先级
const priorities = this.calculatePreloadPriorities(dependencyGraph)
// 设置预加载队列
this.preloadQueue = priorities.map(({ app, priority }) => ({
name: app.name,
entry: app.entry,
priority,
preloadTrigger: this.getPreloadTrigger(app, priority)
}))
// 启动预加载
this.startPreloading()
}
// 计算预加载优先级
calculatePreloadPriorities(dependencyGraph) {
const priorities = []
const visited = new Set()
const calculatePriority = (appName, depth = 0) => {
if (visited.has(appName)) return 0
visited.add(appName)
const dependencies = dependencyGraph.get(appName) || []
// 基于依赖深度和使用频率计算优先级
let priority = 100 - depth * 10
// 考虑历史访问频率
const usage = this.getHistoricalUsage(appName)
priority += usage * 50
// 考虑应用大小
const size = this.getAppSize(appName)
priority -= Math.log(size) * 5
dependencies.forEach(dep => {
priority += calculatePriority(dep, depth + 1) * 0.3
})
return priority
}
dependencyGraph.forEach((deps, appName) => {
const priority = calculatePriority(appName)
priorities.push({
app: { name: appName },
priority
})
})
return priorities.sort((a, b) => b.priority - a.priority)
}
// 启动预加载
async startPreloading() {
// 使用Intersection Observer监听视口
this.setupViewportObserver()
// 使用idle时间预加载
this.scheduleIdlePreload()
// 基于用户行为预加载
this.setupBehaviorPreload()
}
// 视口观察器
setupViewportObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const trigger = entry.target.dataset.preloadTrigger
if (trigger) {
this.triggerPreload(trigger)
}
}
})
}, {
rootMargin: '100px' // 提前100px开始预加载
})
// 观察预加载触发器元素
document.querySelectorAll('[data-preload-trigger]').forEach(el => {
observer.observe(el)
})
}
// 空闲时间预加载
scheduleIdlePreload() {
const preloadNext = () => {
if (this.preloadQueue.length === 0) return
const item = this.preloadQueue.shift()
// 使用requestIdleCallback在空闲时预加载
const callback = (deadline) => {
if (deadline.timeRemaining() > 10) {
this.preloadApp(item.name, item.entry)
.then(() => {
console.log(`Preloaded app: ${item.name}`)
})
.catch(error => {
console.warn(`Preload failed for ${item.name}:`, error)
})
}
// 继续处理下一个
if (this.preloadQueue.length > 0) {
requestIdleCallback(callback)
}
}
requestIdleCallback(callback)
}
// 延迟启动,避免影响初始加载
setTimeout(preloadNext, 2000)
}
// 预加载应用
async preloadApp(appName, entry) {
try {
const startTime = performance.now()
// 预加载资源
await this.preloadResources(entry)
const endTime = performance.now()
// 记录性能指标
this.metrics.record('preload', {
app: appName,
duration: endTime - startTime,
timestamp: Date.now()
})
return true
} catch (error) {
this.metrics.record('preload_error', {
app: appName,
error: error.message,
timestamp: Date.now()
})
throw error
}
}
// 预加载资源
async preloadResources(entry) {
const resources = await this.analyzeResources(entry)
const preloadPromises = resources.map(resource => {
switch (resource.type) {
case 'script':
return this.preloadScript(resource.url)
case 'style':
return this.preloadStylesheet(resource.url)
case 'font':
return this.preloadFont(resource.url)
default:
return this.preloadGeneric(resource.url)
}
})
await Promise.allSettled(preloadPromises)
}
// 预加载脚本
preloadScript(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'preload'
link.as = 'script'
link.href = url
link.crossOrigin = 'anonymous'
link.onload = resolve
link.onerror = reject
document.head.appendChild(link)
})
}
// 智能缓存策略
setupIntelligentCaching() {
// HTTP缓存增强
this.setupHTTPCacheOptimization()
// Service Worker缓存
this.setupServiceWorkerCache()
// 内存缓存管理
this.setupMemoryCache()
}
// Service Worker缓存策略
setupServiceWorkerCache() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/micro-frontend-sw.js')
.then(registration => {
console.log('Service Worker registered:', registration)
// 配置缓存策略
registration.active?.postMessage({
type: 'SETUP_CACHE_STRATEGY',
strategy: {
// 静态资源:缓存优先
static: 'CacheFirst',
// API请求:网络优先
api: 'NetworkFirst',
// 微应用资源:stale-while-revalidate
microApps: 'StaleWhileRevalidate'
}
})
})
.catch(error => {
console.error('Service Worker registration failed:', error)
})
}
}
// 内存缓存管理
setupMemoryCache() {
// LRU缓存实现
this.memoryCache = new LRUCache({
maxSize: 50 * 1024 * 1024, // 50MB
maxAge: 30 * 60 * 1000, // 30分钟
onEvict: (key, value) => {
console.log(`Evicted from memory cache: ${key}`)
}
})
// 定期清理
setInterval(() => {
this.memoryCache.prune()
}, 5 * 60 * 1000) // 每5分钟清理一次
}
// 性能监控
startPerformanceMonitoring() {
// 监听关键性能指标
this.observeWebVitals()
// 监听资源加载性能
this.observeResourceTiming()
// 监听用户交互性能
this.observeUserInteraction()
}
// Web Vitals监控
observeWebVitals() {
// 监听LCP (Largest Contentful Paint)
new PerformanceObserver((entryList) => {
entryList.getEntries().forEach(entry => {
this.metrics.record('lcp', {
value: entry.startTime,
timestamp: Date.now()
})
})
}).observe({ entryTypes: ['largest-contentful-paint'] })
// 监听FID (First Input Delay)
new PerformanceObserver((entryList) => {
entryList.getEntries().forEach(entry => {
this.metrics.record('fid', {
value: entry.processingStart - entry.startTime,
timestamp: Date.now()
})
})
}).observe({ entryTypes: ['first-input'] })
// 监听CLS (Cumulative Layout Shift)
new PerformanceObserver((entryList) => {
let cls = 0
entryList.getEntries().forEach(entry => {
if (!entry.hadRecentInput) {
cls += entry.value
}
})
this.metrics.record('cls', {
value: cls,
timestamp: Date.now()
})
}).observe({ entryTypes: ['layout-shift'] })
}
// 获取历史使用频率
getHistoricalUsage(appName) {
const usage = localStorage.getItem(`micro_app_usage_${appName}`)
return usage ? JSON.parse(usage).frequency : 0
}
// 获取应用大小
getAppSize(appName) {
// 这里应该从构建时分析或API获取
return 1024 * 1024 // 默认1MB
}
// 构建依赖图
buildDependencyGraph(apps) {
const graph = new Map()
apps.forEach(app => {
graph.set(app.name, app.dependencies || [])
})
return graph
}
// 获取预加载触发器
getPreloadTrigger(app, priority) {
if (priority > 80) return 'immediate'
if (priority > 60) return 'viewport'
if (priority > 40) return 'idle'
return 'interaction'
}
// 生成性能报告
generatePerformanceReport() {
return {
cacheStats: this.memoryCache?.getStats() || {},
preloadStats: {
queued: this.preloadQueue.length,
completed: this.metrics.getCount('preload'),
failed: this.metrics.getCount('preload_error')
},
webVitals: {
lcp: this.metrics.getAverage('lcp'),
fid: this.metrics.getAverage('fid'),
cls: this.metrics.getAverage('cls')
},
resourceTiming: this.metrics.getResourceTimingStats()
}
}
}
// 简单的LRU缓存实现
class LRUCache {
constructor(options = {}) {
this.maxSize = options.maxSize || 10 * 1024 * 1024
this.maxAge = options.maxAge || 30 * 60 * 1000
this.onEvict = options.onEvict || (() => {})
this.cache = new Map()
this.currentSize = 0
}
get(key) {
const entry = this.cache.get(key)
if (!entry) return null
// 检查是否过期
if (Date.now() - entry.timestamp > this.maxAge) {
this.delete(key)
return null
}
// 更新访问时间
entry.accessed = Date.now()
// 移到末尾(最近使用)
this.cache.delete(key)
this.cache.set(key, entry)
return entry.value
}
set(key, value) {
const size = this.calculateSize(value)
// 如果单个值太大,直接拒绝
if (size > this.maxSize) {
return false
}
// 删除旧值
if (this.cache.has(key)) {
this.delete(key)
}
// 确保有足够空间
while (this.currentSize + size > this.maxSize && this.cache.size > 0) {
const firstKey = this.cache.keys().next().value
this.delete(firstKey)
}
// 添加新值
this.cache.set(key, {
value,
size,
timestamp: Date.now(),
accessed: Date.now()
})
this.currentSize += size
return true
}
delete(key) {
const entry = this.cache.get(key)
if (entry) {
this.cache.delete(key)
this.currentSize -= entry.size
this.onEvict(key, entry.value)
return true
}
return false
}
clear() {
this.cache.forEach((entry, key) => {
this.onEvict(key, entry.value)
})
this.cache.clear()
this.currentSize = 0
}
prune() {
const now = Date.now()
for (const [key, entry] of this.cache) {
if (now - entry.timestamp > this.maxAge) {
this.delete(key)
}
}
}
calculateSize(value) {
if (typeof value === 'string') {
return value.length * 2 // UTF-16
}
if (value instanceof ArrayBuffer) {
return value.byteLength
}
// 简化的对象大小计算
return JSON.stringify(value).length * 2
}
getStats() {
return {
size: this.cache.size,
currentSize: this.currentSize,
maxSize: this.maxSize,
hitRate: this.hitCount / (this.hitCount + this.missCount) || 0
}
}
}
七、总结与最佳实践
7.1 微前端架构优势
微前端架构通过微内核和插件化思想,为大型前端应用提供了以下核心价值:
- 技术栈多样化: 不同团队可以选择最适合的技术栈
- 独立部署: 微应用可以独立开发、测试和部署
- 团队自治: 减少团队间的技术耦合,提升开发效率
- 渐进式升级: 支持应用的渐进式重构和技术栈升级
7.2 关键设计原则
- 单一职责: 每个微应用专注于特定业务域
- 技术无关: 主应用不应依赖特定的技术栈
- 独立部署: 支持微应用的独立发布和版本管理
- 故障隔离: 单个微应用的故障不应影响其他应用
7.3 最佳实践总结
javascript
// 微前端最佳实践配置
const microFrontendBestPractices = {
// 应用注册配置
registration: {
// 使用配置文件管理应用清单
useConfigFile: true,
// 支持动态发现和注册
dynamicDiscovery: true,
// 应用健康检查
healthCheck: true
},
// 路由管理
routing: {
// 使用History API
mode: 'history',
// 支持嵌套路由
nestedRoutes: true,
// 路由守卫
guards: true
},
// 通信机制
communication: {
// 使用事件总线
eventBus: true,
// 支持命名空间
namespaces: true,
// 消息序列化
serialization: 'json'
},
// 隔离策略
isolation: {
// JavaScript沙箱
sandbox: 'proxy',
// 样式隔离
styles: 'scoped',
// 存储隔离
storage: true
},
// 性能优化
performance: {
// 预加载策略
preload: 'intelligent',
// 缓存策略
cache: 'multi-level',
// 代码分割
codeSplitting: true
},
// 错误处理
errorHandling: {
// 全局错误边界
globalBoundary: true,
// 应用级错误处理
appBoundary: true,
// 错误上报
reporting: true
}
}
通过合理运用微内核架构和插件化思想,微前端为大型前端应用提供了一种可扩展、可维护的解决方案。关键在于平衡应用的独立性与系统的一致性,确保在实现团队自治的同时保持良好的用户体验。