跳到主要内容

Garfish.run

用于初始化全局配置、注册子应用信息,并启动基于路由匹配的子应用自动渲染流程。

garfish 基于 activeWhen 参数自动进行子应用激活匹配,可参考 activeWhen 了解 garfish 路由匹配逻辑;

类型

run(options?: interfaces.Options): Garfish;
// type 定义
export interface Options extends Config, AppGlobalConfig, GlobalLifecycle {}

默认值

  • {}

示例

import Garfish from "garfish";
import type { interfaces } from "garfish";

const config: interfaces.Options = {
/* global options */
basename: '/',
domGetter: '#subApp',
disablePreloadApp: false,
...
/* app infos */
apps: [
{
name: 'react',
activeWhen: '/react',
entry: 'http://localhost:3000',
...
}
],
/* lifecycle hooks */
beforeLoad(appInfo) {
console.log('子应用开始加载', appInfo.name);
},
afterLoad(appInfo) {
console.log('子应用加载完成', appInfo.name);
},
...
}
Garfish.run({ config });

参数

options

domGetter?

  • Type: interfaces.DomGetter
export type DomGetter =
| string
| (() => Element | null)
| (() => Promise<Element>);
  • 子应用的默认挂载点,可选,没有默认值,若省略需要在子应用 AppInfo 中单独指定。二者同时存在时,子应用指定优先级更高;
  • 当提供 string 类型时需要其值是 selector, Garfish 内部会使用 document.querySelector(domGetter) 去选中子应用的挂载点
  • 当提供 string 类型的 domGetter 时,子应用在触发渲染后并不会若当前文档流上并不存在挂载点,Garfish 框架内部在 3s 内轮讯是否有挂载点
  • 当提供函数时,将在子应用挂载过程中执行此函数,并期望返回一个 dom 元素;
  • domGetter 在子应用渲染时无法查询到挂载点,则会丢出 domGetter 无效的异常

basename?

  • Type: string
  • 子应用的基础路径,可选,默认值为全局 basename
  • 通过路由驱动自动加载子应用时实际传递给子应用的 basename 为 basename + activeWhen 计算的值
  • 若手动载入渲染应用时 basename 为实际传入的值
  • 通过 provider 函数basename 参数透传给子应用,子应用需要将 basename 设置为相应子应用的基础路由,这是必须的;
  • 为什么子应用需要设置 basename ?

props?

  • Type: Object
  • 初始化时主应用传递给子应用的数据,可选。子应用 provider 导出函数 生命周期方法中将接收到此数据;

disablePreloadApp?

  • Type: boolean
  • 是否禁用子应用的资源预加载,可选,默认值为 false。默认情况下 Garfish 会开启子应用的资源预加载能力;
  • Garfish 会在用户端计算子应用打开的次数,应用的打开次数越多,预加载权重越大;
  • 预加载能力在弱网情况和手机端将不会开启;

disableSourceListCollect?

  • Type: boolean
  • 是否禁用收集子应用资源列表,可选,默认值为 false。默认情况下 Garfish 会自动收集子应用的资源,用于进行监控分析;

sandbox?

  • Type: SandboxConfig | false 可选,默认值为 全局 sandbox 配置,当设置为 false 时关闭沙箱;

  • SandboxConfig:

interface SandboxConfig {
// 是否开启快照沙箱,默认值为 false:关闭快照沙箱,开启 vm 沙箱
snapshot?: boolean;
// 是否自动以子应用入口的域名前缀对子应用 fetch 请求的进行补齐,默认值为 false
fixBaseUrl?: boolean;
// 是否自动以子应用入口的域名前缀对相对路径资源进行前缀修正,默认值为 true,v1.15.0 版本提供
fixStaticResourceBaseUrl?: boolean;
// 是否开启开启严格隔离,默认值为 false。开启严格隔离后,子应用的渲染节点将会开启 Shadow DOM close 模式,并且子应用的查询和添加行为仅会在 DOM 作用域内进行
strictIsolation?: boolean;
// modules 仅在 vm 沙箱时有效,用于覆盖子应用执行上下文的环境变量,使用自定义的执行上下文,默认值为[]
modules?: Array<Module> | Record<string, Module>;
// disableElementtiming 1.14.4 版本提供,默认值为 false,将会给子应用元素注入 elementtiming 属性,可以通过此属性获取子应用元素的加载时间
disableElementtiming?: boolean;
// fixOwnerDocument 1.17.2 版本提供 ,默认值 false,目前可能会存在 ownerDocument 逃逸的情况,设置为 true 之后将会避免 ownerDocument 逃逸
fixOwnerDocument?: boolean;
}

type Module = (sandbox: Sandbox) => OverridesData | void;

export interface OverridesData {
recover?: (context: Sandbox['global']) => void;
prepare?: () => void;
created?: (context: Sandbox['global']) => void;
override?: Record<PropertyKey, any>;
}
  • 示例
Garfish.run({
sandbox: {
snapshot: false,
strictIsolation: false,
// 覆盖子应用 localStorage,使用当前主应用 localStorage
modules: [
() => ({
override: {
localStorage: window.localStorage,
},
}),
],
},
});
警告

请注意: 如果你在沙箱内自定义的行为将会产生副作用,请确保在 recover 函数中清除你的副作用,garfish 将在应用卸载过程中执行 recover 函数销毁沙箱副作用,否则可能会造成内存泄漏。

  • 在什么情况下我应该关闭 sandbox ?

    Garfish 目前已默认支持沙箱 esModule 能力,若需要在 vm 沙箱支持 esModule 应用,请使用 @garfish/es-module garfish 官方插件支持此能力,但这会带来严重的性能问题,原因。如果你的项目不是很需要在 vm 沙箱下运行,此时可以关闭沙箱;

  • Garfish 沙箱机制

信息

若开启快照沙箱,请注意:

  1. 快照沙箱无法隔离主、子应用
  2. 快照沙箱无法支持多实例(同时加载多个子应用) :::

autoRefreshApp?

  • Type: boolean
  • 主应用在已经打开子应用页面的前提下,跳转子应用的子路由触发子应用的视图更新,默认值为 true
  • 若关闭 autoRefreshApp, 则跳转 子应用子路由 将只能通过 Garfish.router 进行跳转,使用框架自身路由 API(如 react-router)跳转将失效;
  • 在某些场景下,通过主应用触发子应用视图更新可能会导致触发子应用的视图刷新而触发子应用的 hook,所以提供关闭触发子应用视图刷新的能力;

protectVariable?

  • Type: string[]
  • 在开启沙箱的情况下,提供使得 window 上的某些变量处于受保护状态的能力:这些值的读写不会受到沙箱隔离机制的影响,所有应用均可读取到,可选;
  • 若希望在应用间共享 window 上的某些值,可将该值放置在数组中;
  • 该属性与 setGlobalValue 功能相同,推荐使用 protectVariable 属性,通过 protectVariable 可以明确的感知哪些值可能在应用间相互影响;

insulationVariable?

  • Type: string[]
  • 在开启沙箱的情况下,沙箱的子应用的环境变量将会从主应用中继承,举例:
    • 在加载子应用前,主应用有一段:window.xxxx = 123
    • 子应用中可以获取获取主应用的环境变量 console.log(window.xxxx),输出 123
    • 因为目前 Garfish 的主子应用环境是隔离的,但是子应用的环境继承至主应用时可能会造成一些影响不符合预期
  • 若希望在子应用某些环境变量不继承至主应用,可以使用 insulationVariable 配置,例如:
    • insulationVariable: ['xxxx']
    • 'xxxx' 在子应用中将不会继承至主应用,console.log(window.xxxx) 输出 undefined

apps?

  • Type: AppInfo[]
  • 子应用列表信息,此字段参数信息与 registerApp 一致,可跳转查看详细介绍;
export interface AppInfo extends AppConfig, AppLifecycle {}

export interface AppGlobalConfig {
// 子应用的基础路径,通过路由驱动自动加载子应用时实际传递给子应用的 basename 为计算的值
// 若手动载入渲染应用时 basename 为这里指定的值
basename?: string;
// 子应用挂载点,同上,此处会覆盖全局默认的 domGetter
domGetter?: DomGetter;
// 传递给子应用的数据, 同上,此处会覆盖全局默认的 props
props?: Record<string, any>;
// 子应用的沙箱配置,同上,此处会覆盖全局默认的 sandbox
sandbox?: false | SandboxConfig;
}

export type AppConfig = Partial<AppGlobalConfig> & {
// 子应用的名称,需要唯一
name: string;
// 子应用的入口资源地址,支持 HTML 和 JS
entry?: string;
// 是否缓存子应用,默认值为 true;
cache?: boolean;
// 是否检查 provider, 默认为true;
noCheckProvider?: boolean;
};
  • 示例
Garfish.run({
...,
apps: [
{
name: 'vue-app',
basename: '/demo',
activeWhen: '/vue-app',
entry: 'http://localhost:3000',
props: {
msg: 'vue-app msg',
}
}
}
]

beforeLoad

  • Type: async (appInfo: AppInfo, appInstance: App) => false | undefined

    • hook 的参数分别为:应用信息、应用实例;
    • 当返回 false 时将中断子应用的加载及后续流程;
  • Kind: async, sequential

  • Trigger:

    • 在调用 Garfish.load 时触发该 hook
    • 子应用加载前触发,此时还未开始加载子应用资源;
  • 示例

Garfish.run({
...,
beforeLoad(appInfo) {
console.log('子应用开始加载', appInfo.name);
}

afterLoad

  • Type: async (appInfo: AppInfo, appInstance: interfaces.App) => void

  • hook 的参数分别为:应用信息、应用实例;

  • Kind: async, sequential

  • Trigger:

    • 在调用 Garfish.load 后并且子应用加载完成时触发该 hook
  • 示例

Garfish.run({
...,
afterLoad(appInfo) {
console.log('子应用加载完成', appInfo.name);
}
})

errorLoadApp

  • Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App) => void

    • hook 的参数分别为:error 实例、 appInfo 信息、appInstance 应用实例
    • 一旦设置该 hook,子应用加载错误不会 throw 到文档流中,全局错误监听将无法捕获到;
  • Kind: sync, sequential

  • Trigger:

    • 在调用 Garfish.load 过程中,并且加载失败时触发该 hook
  • 示例

Garfish.run({
...,
errorLoadApp(error, appInfo) {
console.log('子应用加载异常', appInfo.name);
console.error(error);
}
})

beforeMount

  • Type: (appInfo: AppInfo, appInstance: interfaces.App, cacheMode: boolean) => void

    • hook 的参数分别为:appInfo 信息、appInstance 应用实例、是否为 缓存模式 渲染和销毁
  • Kind: sync, sequential

  • Previous Hook: beforeEvalafterEval

  • Trigger:

    • 此时子应用资源准备完成,运行时环境初始化完成,准备开始渲染子应用 DOM 树;
    • 在调用 app.mountapp.show 触发该 hook,用户除了手动调用这两个方法外,Garfish Router 托管模式还会自动触发
      • 在使用 app.mount 渲染应用是 cacheModefalse
      • 在使用 app.show 渲染应用是 cacheModetrue
  • 示例

Garfish.run({
...,
beforeMount(appInfo) {
console.log('子应用开始渲染', appInfo.name);
}
})

afterMount

  • Type: (appInfo: AppInfo, appInstance: interfaces.App, cacheMode: boolean) => void

    • hook 的参数分别为:appInfo 信息、appInstance 应用实例、是否为 缓存模式 渲染和销毁
  • Kind: sync, sequential

  • Previous Hook: beforeLoadafterLoadbeforeMount

  • Trigger:

    • 此时子应用 DOM 树已渲染完成,garfish 实例 activeApps 中已添加当前子应用 app 实例;
    • 在挂载过程中,会调用应用生命周期中的 render 函数,用户可在挂载前定义相关操作;
    • 若挂载过程中出现异常,会触发 errorMountApp,同时会清除已创建的 app 渲染容器 appContainer
  • 示例

Garfish.run({
...,
afterMount(appInfo) {
console.log('子应用渲染结束', appInfo.name);
}
})

beforeEval

  • Type: (appInfo: AppInfo, code: string, env: Record<string, any>, url: string, options) => void

    • hook 的参数分别为:appInfo 信息、code 执行的代码、env 要注入的环境变量,url 代码的资源地址、options 参数选项(例如 async 是否异步执行、noEntry 是否是 noEntry 模式);
  • Kind: sync, sequential

  • Previous Hook: beforeMount

  • Trigger:

    • 在子应用挂载过程中、实际执行代码前触发该 hook;
    • 应用 html 内的 script 和动态创建的脚本执行时都会触发该 hook
    • 此时 DOM 树已添加至文档流中,子应用代码准备执行;
    • 若代码执行过程中抛出异常,则将触发 errorMountApp,否则触发 beforeEval
  • 示例

Garfish.run({
...,
beforeEval(appInfo) {
console.log('子应用代码开始执行', appInfo.name);
}
})

afterEval

  • Type: (appInfo: AppInfo, code: string, env: Record<string, any>, url: string, options) => void

    • hook 的参数分别为:appInfo 信息、code 执行的代码、env 要注入的环境变量,url 应用访问地址、options 参数选项例如 async 是否异步执行、noEntry 是否是 noEntry 模式;
  • Kind: sync, sequential

  • Previous Hook: beforeLoadafterLoad

  • Trigger:

    • 在实际执行代码后。afterMount 触发前触发;
    • 子应用 html 内的 script 和动态创建的脚本执行时都会触发该 hook
  • 示例

Garfish.run({
...,
afterEval(appInfo) {
console.log('子应用代码执行完成', appInfo.name);
}
})

errorMountApp

  • Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App) => void

    • 一旦设置该 hook,子应用加载错误不会 throw 到文档流中,全局错误监听将无法捕获到;
  • Kind: sync, sequential

  • Previous Hook: beforeLoadafterLoadbeforeMountafterMount

  • Trigger:

    • 在渲染过程中出现异常会触发该 hook,子应用同步执行的代码出现异常会触发该 hook,异步代码无法触发
  • 示例

Garfish.run({
...,
errorMountApp(error, appInfo) {
console.log('子应用渲染异常', appInfo.name);
console.error(error);
}
})

beforeUnmount

  • Type: ( appInfo: AppInfo, appInstance: interfaces.App) => void
  • Kind: sync, sequential
  • Previous Hook: beforeLoadafterLoadbeforeMountafterMount
  • Trigger:
    • 在调用 app.unmountapp.hide 触发该 hook,用户除了手动调用这两个方法外,Garfish Router 托管模式还会自动触发
      • 在使用 app.unmount 渲染应用是 cacheModefalse
      • 在使用 app.hide 渲染应用是 cacheModetrue
    • 此时子应用 DOM 元素还未卸载,副作用尚未清除;
    • 此时子应用 DOM 树已渲染完成,garfish 实例 activeApps 中已添加当前子应用 app 实例;

afterUnmount

  • Type: ( appInfo: AppInfo, appInstance: interfaces.App) => void
  • Kind: sync, sequential
  • Trigger:
    • 此时,应用在渲和运行过程中产生的副作用已清除,DOM 已卸载,沙箱副作用已清除,garfish 实例 activeApps 当前 app 已移除;
    • 在应用销毁过程中会调用应用生命周期中的 destory 函数,用户可在销毁前定义相关操作;
    • 若应用卸载过程中出现异常,会触发 errorUnmountApp

errorUnmountApp

  • Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App)=> void

    • 一旦设置该 hook,子应用销毁错误不会向上 throw 到文档流中,全局错误监听将无法捕获到;
  • Kind: sync, sequential

  • Trigger:

    • app.unmountapp.hide 销毁过程中出现异常则会触发该 hook,用户除了手动调用这两个方法外,Garfish Router 托管模式还会自动触发
  • 示例

Garfish.run({
...,
errorUnmountApp(error, appInfo) {
console.log('子应用销毁异常', appInfo.name);
console.error(error);
}
})

onNotMatchRouter

  • Type: (path: string)=> void

    • hook 的参数分别为:应用信息、应用实例;
  • Kind: sync, sequential

  • Trigger:

    • 路由发生变化当前未激活子应用且未匹配到任何子应用时触发
  • 示例

Garfish.run({
...,
onNotMatchRouter(path) {
console.log('未匹配到子应用', path);
}
})