认识
一、认识
Single-Spa 是一个用于构建微前端架构的框架,它允许你将多个独立的前端应用(即子应用)集成在同一个页面中,并对它们的生命周期进行管理。single-spa
通过监听 url change
事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。同时single-spa
要求子应用修改渲染逻辑并暴露出三个方法:bootstrap
、mount
、unmount
,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。但是, Single-spa
主要专注于微前端架构中应用的生命周期管理和协调加载,它本身并不提供完全的沙箱或隔离机制。
二、特点
三、问题
3.1 认识 SingleSpa?
Single-Spa 是一个用于构建微前端架构的框架,它允许你将多个独立的前端应用(即子应用)集成在同一个页面中,并对它们的生命周期进行管理。single-spa
通过监听 url change
事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。同时single-spa
要求子应用修改渲染逻辑并暴露出三个方法:bootstrap
、mount
、unmount
,分别对应初始化、渲染和卸载,这也导致子应用需要对入口文件进行修改。但是, Single-spa
主要专注于微前端架构中应用的生命周期管理和协调加载,它本身并不提供完全的沙箱或隔离机制。
3.2 SingleSpa 是如何实现的?
SingleSpa
是一个用于构建微前端架构的框架,它允许你将多个独立的前端应用(即子应用)集成在同一个页面中,并对它们的生命周期进行管理。Single-spa
并不内置完善的隔离机制, 它的重点在于调度和管理微应用的加载、卸载和生命周期。SingleSpa
的实现原理
一、应用注册与配置:
-
注册应用,
SingleSpa
提供了registerApplication
接口,用于注册微应用。每个微应用需要配置以下内容: 应用名称, 唯一标识该应用; 加载函数(loadApp
), 返回一个Promise
,加载微应用的代码(通常返回一个包含生命周期函数的模块); 激活规则(activeWhen
), 一个函数或数组,用来判断当前URL
是否满足激活该应用的条件; 自定义属性(customProps
), 传递给微应用的自定义配置,可选。 -
生命周期, 每个微应用都需要实现一组标准的生命周期方法:
bootstrap
, 应用初始化阶段,只执行一次,用于设置应用初始状态;mount
, 将应用挂载到DOM
上,应用进入活跃状态;unmount
, 将应用从DOM
卸载,进入非活跃状态;update
, 用于应用已经挂载后,接收更新(例如路由参数变化);
二、路由监控与激活判定: 1. 全局路由监听, SingleSpa
在内部通过监听浏览器路由变化(如 popstate
、hashchange
以及通过 history API
触发的变化),捕获 URL
变化事件; 2. 激活判断, 每当路由变化时,SingleSpa
遍历已注册的应用,根据它们的 activeWhen
规则判断哪些应用应当处于激活状态。对于当前不活跃的已挂载应用,会调用其 unmount
方法。对于满足激活条件但未加载或未挂载的应用,会按顺序执行加载、初始化(bootstrap
)、挂载(mount
)流程。
三、生命周期管理:
-
异步流程管理, 由于各个生命周期方法可能是异步的,
SingleSpa
采用Promise
机制来确保生命周期之间顺序正确: 1. 加载(loadApp
), 获取应用代码模块; 2. 初始化(bootstrap
), 调用模块的bootstrap
方法,初始化一次; 3. 挂载(mount
), 调用mount
方法,将应用渲染到页面中。4. 卸载(unmount
), 当应用不再活跃时,调用unmount
方法。 -
错误处理与隔离, 每个生命周期步骤都应捕获错误,防止某个子应用出错影响整个页面。
SingleSpa
也提供了相关 API 供开发者处理错误。
3.3 如果让你模拟 SingleSpa, 该怎么实现?
一、构建应用注册机制: 创建一个全局注册表来保存所有微应用的配置信息
const registeredApps = [];
function registerApplication(name, loadApp, activeWhen, customProps = {}) {
registeredApps.push({
name,
loadApp,
activeWhen,
customProps,
status: 'NOT_LOADED', // NOT_LOADED, BOOTSTRAPPED, MOUNTED, UNMOUNTED
bootstrap: null,
mount: null,
unmount: null
});
}
二、监听路由变化, 监听 popstate
和 hashchange
事件(也可以重写 history.pushState
以捕获编程式路由变化)
function handleRouteChange() {
// 判断哪些应用应该激活
const activeApps = registeredApps.filter(app => app.activeWhen(window.location));
// 卸载不再活跃的应用
registeredApps.forEach(app => {
if (app.status === 'MOUNTED' && !app.activeWhen(window.location)) {
app.unmount(app.customProps).then(() => {
app.status = 'UNMOUNTED';
}).catch(err => console.error(`Error unmounting ${app.name}:`, err));
}
});
// 加载并挂载新的激活应用
activeApps.forEach(app => {
if (app.status === 'NOT_LOADED' || app.status === 'UNMOUNTED') {
app.loadApp().then(module => {
// 假设 module 包含 bootstrap, mount, unmount 方法
app.bootstrap = module.bootstrap;
app.mount = module.mount;
app.unmount = module.unmount;
return app.bootstrap(app.customProps);
}).then(() => {
return app.mount(app.customProps);
}).then(() => {
app.status = 'MOUNTED';
}).catch(err => console.error(`Error mounting ${app.name}:`, err));
}
});
}
window.addEventListener('popstate', handleRouteChange);
window.addEventListener('hashchange', handleRouteChange);
三、初始化启动, 在页面加载时,调用一次 handleRouteChange
来根据当前 URL
挂载需要的应用:
// 页面加载时执行一次路由检查
handleRouteChange();
四、示例微应用
// 假设这是一个微应用模块(例如 remoteApp.js)
export function bootstrap(props) {
console.log('Bootstrap with', props);
return Promise.resolve();
}
export function mount(props) {
console.log('Mounting app with', props);
// 渲染到 DOM 中
const container = document.getElementById('micro-app');
container.innerHTML = `<div>Micro App Content</div>`;
return Promise.resolve();
}
export function unmount(props) {
console.log('Unmounting app with', props);
const container = document.getElementById('micro-app');
container.innerHTML = '';
return Promise.resolve();
}
通过 registerApplication
注册这个微应用,并设置激活条件(例如 URL
包含 /app
时激活):
registerApplication(
'my-micro-app',
() => import('./remoteApp.js'),
(location) => location.pathname.startsWith('/app'),
{ someProp: 'value' }
);
这样,一个基本的 SingleSpa
模拟就完成了。核心思路在于:
-
注册微应用及其生命周期函数。
-
监听路由变化,根据激活条件进行应用加载、初始化、挂载或卸载。
-
使用
Promise
管理异步生命周期函数,确保顺序正确并处理错误。