Garfish 支持哪些子应用加载方式,如何根据场景选择合适的加载策略?
Garfish 子应用的加载方式主要分为路由驱动自动加载和手动控制加载两种模式,配合内置的预加载与缓存机制,可以覆盖从核心业务到低频功能的全场景需求。
一、两种核心加载模式
1. 路由驱动自动加载
通过 Garfish.run() 注册子应用并配置 activeWhen 路由匹配规则,Garfish 会自动劫持路由,当浏览器 URL 命中时加载并挂载对应子应用。这是最常用的方式,适合子应用与路由强关联的场景。
typescriptimport Garfish from 'garfish'; Garfish.run({ basename: '/', domGetter: '#subApp', apps: [ { name: 'react-app', activeWhen: '/react', entry: 'http://localhost:3000', }, { name: 'vue-app', activeWhen: '/vue', entry: 'http://localhost:8080/index.js', // 也支持 JS 入口 }, ], });
关键配置项:
| 参数 | 说明 |
|---|---|
activeWhen | 路由匹配条件,支持字符串、正则或函数 |
entry | 子应用入口地址,支持 HTML 入口和 JS 入口两种格式 |
domGetter | 子应用挂载的 DOM 容器 |
basename | 基础路径,实际传给子应用的 basename 为 basename + activeWhen |
2. 手动控制加载
通过 Garfish.loadApp() 手动加载子应用,灵活控制挂载、显示、隐藏的时机。适合子应用不依赖路由、需要动态挂载到任意容器的场景,比如弹窗内嵌子应用、Tab 切换复用同一子应用等。
typescriptimport Garfish from 'garfish'; // 手动加载子应用 const app = await Garfish.loadApp('vue-app', { domGetter: '#container', entry: 'http://localhost:3000', cache: true, }); // 首次渲染调用 mount,后续切换调用 show app.mounted ? app.show() : await app.mount(); // 隐藏子应用(保留实例,不销毁) await app.hide(); // 完全卸载子应用 await app.unmount();
mount() 与 show() 的区别: mount() 是首次渲染,会执行子应用的生命周期;show() 是将已挂载的子应用重新显示,跳过生命周期执行,切换更轻量。路由插件内部的核心判断逻辑是:当 cache 为 true 且 app.mounted 为 true 时调用 show(),否则调用 mount()。
二、预加载机制
Garfish 内置了智能预加载能力,在主应用空闲时提前拉取子应用资源,用户真正访问时无需等待网络请求。
自动预加载
默认开启(disablePreloadApp: false),Garfish 会在用户端统计子应用的打开频率,打开次数越多的子应用预加载权重越高。在弱网环境和移动端会自动关闭预加载以节省流量。
手动预加载
使用 Garfish.preloadApp() 主动触发指定子应用的资源预加载,适合在主应用 HTML 阶段就提前拉取首屏需要的核心子应用:
typescriptimport Garfish from 'garfish'; // 先注册子应用 Garfish.registerApp({ name: 'react', entry: 'http://localhost:3000', }); // 预加载 react 子应用的入口资源和子资源 Garfish.preloadApp('react');
预加载的资源存储在独立内存中,真正加载子应用时不会再发起资源请求,直接复用已缓存的静态资源。
关闭预加载
如果不需要预加载(如子应用体积大且访问频率低),可以在 Garfish.run() 中配置:
typescriptGarfish.run({ disablePreloadApp: true, // 关闭预加载 // ... });
三、缓存机制
Garfish 默认开启子应用缓存(cache: true),已加载的子应用实例不会在切换时销毁,而是保留在内存中。再次激活时调用 show() 而非 mount(),显著减少重复渲染开销。
可以进一步配置缓存策略:
typescriptconst app = await Garfish.loadApp('vue-app', { cache: true, cacheOptions: { maxAge: 15 * 60 * 1000, // 缓存有效期 15 分钟 }, });
如果子应用存在内存泄漏问题或需要每次重新初始化,可以关闭缓存:
typescriptGarfish.run({ apps: [ { name: 'problematic-app', activeWhen: '/problem', entry: 'http://localhost:4000', cache: false, // 每次切换都销毁并重建 }, ], });
四、加载生命周期钩子
Garfish 提供了 beforeLoad 和 afterLoad 钩子,可以在子应用加载前后执行自定义逻辑,比如埋点统计、权限校验、加载态展示等:
typescriptGarfish.run({ beforeLoad(appInfo) { console.log('子应用开始加载:', appInfo.name); showLoadingSpinner(); }, afterLoad(appInfo) { console.log('子应用加载完成:', appInfo.name); hideLoadingSpinner(); }, });
五、如何根据场景选择加载策略
场景一:常规路由级子应用
选择:路由驱动自动加载 + 默认预加载 + 默认缓存
这是最典型的微前端接入方式。子应用与路由一一对应,Garfish 自动处理加载、挂载、卸载的全流程:
typescriptGarfish.run({ basename: '/', domGetter: '#subApp', apps: [ { name: 'crm', activeWhen: '/crm', entry: 'http://localhost:3001' }, { name: 'oa', activeWhen: '/oa', entry: 'http://localhost:3002' }, ], });
场景二:首屏核心子应用需要极速加载
选择:路由驱动自动加载 + 手动 preloadApp 提前拉取
在主应用 HTML 阶段就预加载首屏核心子应用,确保用户进入时资源已经就绪:
typescript// 在主应用最早执行的脚本中预加载 Garfish.registerApp({ name: 'home', entry: 'http://localhost:3001' }); Garfish.preloadApp('home'); Garfish.run({ domGetter: '#subApp', apps: [{ name: 'home', activeWhen: '/home', entry: 'http://localhost:3001' }], });
场景三:子应用需要挂载到非路由驱动的容器
选择:手动 loadApp 加载
比如侧边栏中嵌入的子应用、弹窗中加载的子应用,路由不变但需要动态挂载:
typescriptconst sidebarApp = await Garfish.loadApp('sidebar-widget', { domGetter: '#sidebar', entry: 'http://localhost:3003', cache: true, }); await sidebarApp.mount();
场景四:低频大型子应用
选择:路由驱动自动加载 + 关闭预加载 + 关闭缓存
低频使用的子应用不需要预加载占用带宽,也不需要缓存占用内存:
typescriptGarfish.run({ disablePreloadApp: true, // 如需全部关闭 apps: [ { name: 'admin-panel', activeWhen: '/admin', entry: 'http://localhost:3004', cache: false, }, ], });
场景五:多实例同类型子应用
选择:手动 loadApp 加载 + 不同容器
需要在同一页面同时展示多个同类型子应用实例时,路由驱动无法满足,必须手动控制:
typescriptconst app1 = await Garfish.loadApp('chart', { domGetter: '#chart-container-1', entry: 'http://localhost:3005', }); const app2 = await Garfish.loadApp('chart', { domGetter: '#chart-container-2', entry: 'http://localhost:3005', }); await Promise.all([app1.mount(), app2.mount()]);
六、常见问题
Q: loadApp 提示 "Invalid domGetter" 怎么办?
确保挂载节点已经存在于页面 DOM 中。在 Garfish 开始渲染时如果查询不到挂载节点,就会抛出此错误。可以在组件的 mounted 生命周期或 useEffect 回调中调用 loadApp。
Q: 子应用切换后状态丢失怎么办?
默认情况下 cache: true,子应用切换时调用 hide() 而非 unmount(),状态会保留。如果状态丢失,检查是否误将 cache 设为 false,或子应用内部在 unmount 生命周期中手动清理了状态。
Q: 预加载在移动端不生效?
Garfish 在弱网环境和移动端会自动关闭预加载,这是预期行为。如需强制开启,需修改 Garfish 源码中的网络检测逻辑,但不建议这样做。