5月27日 20:04

Garfish 的生命周期钩子有哪些?provider 函数和 show/hide 怎么用?

Garfish 子应用的生命周期围绕 provider 函数展开,核心钩子按执行顺序为:bootstrap → mount → update(可选) → unmount,另有 show/hide 用于缓存场景。与 qiankun 的最大区别在于:Garfish 子应用必须导出 provider 函数而非直接导出生命周期函数。

核心钩子及执行顺序

钩子触发时机调用次数作用
bootstrap子应用首次加载仅 1 次初始化配置、注入依赖
mount子应用渲染到容器每次激活挂载 DOM、启动渲染
unmount子应用从页面移除每次离开清理 DOM、事件、定时器
update父应用传递 props 变更(可选)按需响应属性更新
show缓存子应用重新显示(可选)按需恢复运行状态
hide缓存子应用被隐藏(可选)按需暂停但不销毁

执行顺序:

  • 首次加载:provider() → bootstrap → mount
  • 路由切换离开:unmount(非缓存)或 hide(缓存模式)
  • 路由切换回来:mount(非缓存)或 show(缓存模式,跳过 bootstrap)
  • 属性变更:update
  • 彻底销毁:unmount

provider 函数:Garfish 生命周期的入口

Garfish 子应用必须导出一个 provider 函数,它的返回值才是真正的生命周期对象:

javascript
// 子应用入口 export function provider({ basename, dom, ...props }) { return { bootstrap() { console.log('[sub-app] bootstrap, basename:', basename); return Promise.resolve(); }, mount({ basename, dom }) { const container = dom.querySelector('#app'); ReactDOM.render(<App basename={basename} />, container); return Promise.resolve(); }, unmount({ dom }) { const container = dom.querySelector('#app'); ReactDOM.unmountComponentAtNode(container); return Promise.resolve(); }, update({ ...newProps }) { // 响应主应用传入的属性变更 return Promise.resolve(); }, }; }

关键点:provider 接收主应用传入的 props(如 basename、dom 容器),在 mount/unmount 中通过参数获取运行时上下文,而非闭包变量。

show/hide:缓存模式下的生命周期

当主应用配置 sandbox.cache = true 时,子应用不会被销毁,而是通过 show/hide 控制显隐:

javascript
export function provider() { let app = null; return { // ...bootstrap, mount, unmount 省略 show() { // 恢复定时器、重新订阅事件、恢复动画 console.log('[sub-app] show: 恢复运行状态'); return Promise.resolve(); }, hide() { // 暂停定时器、取消事件订阅、暂停动画(不销毁 DOM) console.log('[sub-app] hide: 暂停运行状态'); return Promise.resolve(); }, }; }

缓存模式下 show/hide 与 mount/unmount 互斥:激活走 show(不走 mount),离开走 hide(不走 unmount)。

完整生命周期流程图

shell
首次加载: 下载子应用 JS → 执行沙箱隔离 → 调用 provider() → bootstrap() → mount() 路由切换(非缓存): 旧子应用 unmount() → 新子应用 mount() 路由切换(缓存模式): 旧子应用 hide() → 新子应用 mount() 或 show() 属性更新: 主应用 setProps() → 子应用 update() 彻底销毁: unmount() → 清理沙箱 → 释放内存

插件级生命周期钩子

除子应用生命周期外,Garfish 还提供主应用侧的插件钩子,用于拦截加载过程:

javascript
Garfish.run({ plugins: [ () => ({ beforeLoad(appInfo) { console.log('即将加载:', appInfo.name); return appInfo; }, afterLoad(appInfo) { console.log('加载完成:', appInfo.name); }, beforeMount(appInfo) { console.log('即将挂载:', appInfo.name); }, afterMount(appInfo) { console.log('挂载完成:', appInfo.name); }, beforeUnmount(appInfo) { console.log('即将卸载:', appInfo.name); }, afterUnmount(appInfo) { console.log('卸载完成:', appInfo.name); }, }), ], });

这些钩子在主应用侧执行,可用于日志采集、性能监控、权限校验等横切逻辑。

与 qiankun 生命周期的对比

对比项Garfishqiankun
导出方式provider 函数返回生命周期对象直接导出 bootstrap/mount/unmount
缓存钩子show/hide无(需自行实现)
插件钩子beforeLoad/afterLoad 等 6 个框架级 beforeLoad/afterMount 等
参数传递provider(props) + mount(props)mount(props)
沙箱集成生命周期与沙箱强绑定沙箱独立于生命周期

常见踩坑与解决方案

1. mount 中拿不到容器 DOM

mount 回调中的 dom 参数是 Garfish 创建的容器,需要在 dom 内查找挂载点:

javascript
mount({ dom }) { // 错误:document.getElementById('app') // 正确:在 Garfish 提供的 dom 内查找 const container = dom.querySelector('#sub-app-root'); ReactDOM.render(<App />, container); }

2. unmount 后仍然有内存泄漏

定时器和全局事件监听不会随 DOM 移除而自动清理:

javascript
let timer = null; let resizeHandler = null; mount({ dom }) { timer = setInterval(sendHeartbeat, 5000); resizeHandler = () => recalculateLayout(); window.addEventListener('resize', resizeHandler); // ... }, unmount() { clearInterval(timer); window.removeEventListener('resize', resizeHandler); timer = null; resizeHandler = null; }

3. 缓存模式下 show/hide 未实现导致状态异常

如果开启缓存但只实现了 mount/unmount,子应用在 hide 后定时器仍在运行、事件仍在监听,切回时可能出现重复绑定。必须配套实现 show/hide。

追问

Q: Garfish 为什么选择 provider 函数模式,而不是像 qiankun 那样直接导出生命周期?

provider 模式有两个优势:一是每次加载都可以通过 provider 重新创建生命周期实例,避免单例模式下多次挂载的状态污染;二是 provider 在执行时可以拿到主应用传入的 props(如 basename、dom),在闭包中天然拥有运行时上下文,不需要在 mount 中额外合并参数。

Q: 如果子应用不实现 unmount 会怎样?

子应用的 DOM 不会从容器中移除,事件监听器和定时器继续运行,路由切换后旧应用的副作用仍在执行,会导致内存泄漏、事件重复触发、UI 叠加渲染等问题。Garfish 不会强制校验 unmount 的实现,这是开发者的责任。

Q: bootstrap 和 mount 的区别是什么,能不能把初始化逻辑都放在 mount 里?

bootstrap 只执行一次,mount 每次激活都会执行。如果把初始化逻辑(如加载配置、注册全局插件)放在 mount 里,每次路由切回都会重复执行,既浪费性能又可能导致重复注册。正确的做法是:一次性初始化放 bootstrap,每次挂载都需要的渲染逻辑放 mount。

标签:Garfish