简单理解微前端
参考文章 手把手教你写一个简易的微前端框架-谭光志
参考仓库 mini-single-spa
本篇文章为学习微前端有感,只为记录微前端简短理解只实现了v1&v2版本,深度以及广度完全不及大佬文章
微前端是什么
微前端是一种软件架构模式,旨在将前端应用程序拆分为更小的、可独立开发、部署和维护的微服务单元。与传统的单体前端应用不同,微前端允许团队独立开发和部署其特定的前端功能模块,这些模块可以独立运行,也可以与其他模块协同工作,形成一个完整的前端应用
微前端应用运行原理
微前端分主应用和子应用,在主应用中来注册子应用,通过监听路由的变化来挂载或者卸载子应用,从这里可知,子应用都有其对应的生命周期
子应用注册
我们需要暴露出一个方法来使主应用注册子应用,并通过这个方法来接收到子应用的部分信息,如子应用名称,对应的路由地址,以及子应用生命周期函数等等
在微前端内部,通过一个数组来接收主应用注册的子应用,当子应用被注册时会进行应用状态的更新为启动前,并将注册的应用收集起来
/**
* 该方法用来注册新的应用程序,并将应用程序的状态改为BEFORE_BOOTSTRAP(初始化状态)
* @param app 所注册的应用
*/
export default function registerApplication(app: Application) {
console.log('app registerApplication', app)
if (typeof app.activeRule === 'string') {
const path = app.activeRule
/**
* 并且判断activeRule是否为字符串,如果 app.activeRule 是一个字符串,
* 代码将它赋值给一个新的函数 app.activeRule,这个函数会接受一个 location 参数
* (默认为 window.location),并检查当前页面的路径是否与传入的路径匹配。
* 这是为了将路径规则转换为一个可执行的条件函数。
* @param location 地址
* @returns
*/
app.activeRule = (location = window.location) => location.pathname === path
}
app.status = AppStatus.BEFORE_BOOTSTRAP
apps.push(app)
}
子应用加载
在子应用的加载中会根据获取到的应用的状态来进行加载
/ 已注册应用集合
export const apps: Application[] = []
export async function loadApps() {
// 获取状态为MOUNTED的应用
const toUnMountApp = getAppsWithStatus(AppStatus.MOUNTED)
// 对状态为MOUNTED的应用调用unMountApp方法 进行应用程序卸载
await Promise.all(toUnMountApp.map(unMountApp))
// 获取状态为BEFORE_BOOTSTRAP的应用
const toLoadApp = getAppsWithStatus(AppStatus.BEFORE_BOOTSTRAP)
// 对状态为BEFORE_BOOTSTRAP的应用调用bootstrop函数 进行初始化
await Promise.all(toLoadApp.map(bootstrapApp))
const toMountApp = [
...getAppsWithStatus(AppStatus.BOOTSTRAPPED),
...getAppsWithStatus(AppStatus.UNMOUNTED),
]
await toMountApp.map(mountApp)
}
/**
* 这个函数用于获取具有特定状态的应用程序的列表。它被多次调用,
* 传入不同的 AppStatus 枚举值作为参数,从而获取不同状态的应用程序列表。
* 这可能是一个用于筛选应用程序的辅助函数。
* @param status 应用状态
* @returns
*/
function getAppsWithStatus(status: AppStatus) {
// 收集筛选后的应用
const result: Application[] = []
apps.forEach((app) => {
// tobootstrap or tomount
if (isActive(app) && app.status === status) {
switch (app.status) {
case AppStatus.BEFORE_BOOTSTRAP:
case AppStatus.BOOTSTRAPPED:
case AppStatus.UNMOUNTED:
result.push(app)
break
}
} else if (
app.status === AppStatus.MOUNTED
&& status === AppStatus.MOUNTED
) {
// tounmount
result.push(app)
}
})
return result
}
function isActive(app: Application) {
return typeof app.activeRule === 'function' && app.activeRule(window.location)
}
路由监听
在路由监听这一层中,主要是对pushState
,replaceState
两个方法的重写,对这两个方法添加额外的行为,来进行子应用的加载或卸载,
const originPushState = window.history.pushState
const originReplaceState = window.history.replaceState
export default function overwriteEventAndHistory() {
window.history.pushState = function (
data: any,
unused: string,
url?: string | URL | null | undefined
): void {
const result = originPushState.call(this, data, unused, url)
loadApps() // 加载或卸载应用
return result
}
window.history.replaceState = function (
data: any,
unused: string,
url?: string | URL | null | undefined
) {
const result = originReplaceState.call(this, data, unused, url)
loadApps() // 加载或卸载应用
return result
}
window.addEventListener('popstate', () => {
console.log('popstate')
})
window.addEventListener('hashChange', () => {
console.log('hashChange')
})
}
生命周期
子应用的生命周期分为bootstrap,mount,unmount三个部分,启动,挂载和卸载
bootstrap
bootstrap是用于启动应用程序的核心函数,负责应用程序的初始化和启动过程。
在此会先从对应的微前端应用中加载其声明的生命周期函数和属性,并对这些核心的声明周期函数进行校验,校验通过后将加载的生命周期函数赋值给对应的属性
接下来初始化应用的props,并执行应用程序的bootstrap函数,并处理结果
执行后修改应用的状态
mount
在mount执行时首先会将应用程序的状态改为挂载前,接下来调用应用的mount函数执行生命周期内的函数
执行修改应用的状态为挂载中
unmount
将应用程序的状态改为卸载前,执行函数的卸载操作,执行完后将应用程序状态改为已卸载
简单注册使用
registerApplication({
name: 'vue',
actionRule: '/vue',
loadApp() {
return Promise.resolve({
bootstrap() {
console.log('vue bootstrap');
},
mount() {
console.log('vue mount');
},
unmount() {
console.log('vue unmount');
}
})
}
})
registerApplication({
name: 'react',
actionRule: '/react',
loadApp() {
return Promise.resolve({
bootstrap() {
console.log('react bootstrap');
},
mount() {
console.log('react mount');
},
unmount() {
console.log('react unmount');
}
})
}
})