实例
FlamoInstance 是 Runtime 中真正运行的对象。它拥有当前 props、局部状态、节点 identity、事件订阅、child graph 和资源所有权,并提供 set()、on()、call()、slot、variant、inspect 与 destroy 等运行时能力。
挂载是创建 Instance 时完成的,不是创建后的第二步。当前 FlamoInstance 没有 mount() 方法,因此下面的写法不可用:
const first = await runtime.create({ artifact });await first.mount(document.querySelector('#first')); // ❌ FlamoInstance 没有 mount()请使用以下任一种等价写法:
// 写法一:把 target、Artifact 和运行参数分别传给 runtime.mount()const first = await runtime.mount('#first', artifact, { props: { label: 'A' },});
// 写法二:把 target 一起传给 runtime.create()const secondTarget = document.querySelector('#second');if (!secondTarget) throw new Error('找不到 #second');
const second = await runtime.create({ target: secondTarget, artifact, props: { label: 'B' },});runtime.mount(target, artifact, options) 是 runtime.create({ ...options, target, artifact }) 的便捷写法。两者都会创建并返回一个新的 Instance;来自同一 Artifact 的多个 Instance 仍有独立的 id、props、变量覆盖、节点映射和生命周期。
只调用 runtime.create({ artifact }) 也合法。如果 Runtime 没有配置默认 canvas,该 Instance 不会自动附着到 DOM,而且不能稍后再调用 instance.mount() 补挂载;需要 DOM 宿主时,应在创建时传入 target。
挂载目标有什么要求
Section titled “挂载目标有什么要求”target 的公开类型是 Element | HTMLCanvasElement | string:
| 目标 | Runtime 的处理方式 |
|---|---|
CSS selector 字符串,例如 '#first' |
在浏览器中调用 document.querySelector();找不到目标时抛出 flamo.mount: target not found。没有 document 的环境不能使用字符串目标。 |
HTMLCanvasElement |
直接把该 Canvas 作为渲染表面,不再创建子 Canvas。 |
其他 Element |
在目标元素内创建并追加一个由 Runtime 管理的 <canvas>,默认样式为 display: block; width: 100%; height: 100%。 |
实际使用时还应满足以下条件:
- 目标必须在调用前存在。
document.querySelector()的返回类型包含null,应先判空;直接传 selector 字符串可以在找不到节点时得到明确错误。 - 非 Canvas 目标必须能够接收
<canvas>子节点。通常应使用普通HTMLElement,并给它可计算的宽高;脱离文档或尺寸为零的元素即使能创建 Instance,也不会产生可见画面。 Document、Window、ShadowRoot和null不是合法的公开挂载目标。需要在 Shadow DOM 中渲染时,请传入 ShadowRoot 内部的元素或 Canvas。- 同一个 Canvas 可以在 shared Runtime 中承载多个隔离 Instance。此时不要依赖
runtime.get(canvas)区分它们,因为按 Element 查询只能返回当前登记的一个 Instance;请保留 Instance 变量或使用唯一 id 查询。
如果在 createFlamoRuntime({ canvas }) 或 appConfig.canvas 中配置了 Canvas,runtime.create({ artifact }) 会使用该默认 Canvas;这与把普通 DOM 容器作为 target、让 Runtime 自动创建 Canvas 是两种不同的接入方式。
Instance 可以跨脚本文件引用吗
Section titled “Instance 可以跨脚本文件引用吗”可以,但要区分 JavaScript 模块引用 与 Flamo 交付物导入/导出。
FlamoInstance 是运行时对象,也可以像其他 JavaScript 对象一样保存在变量中。在同一个页面、同一个 JavaScript realm 和同一份模块图内,TS/JS 文件可以用标准 ESM 导出并导入这个对象引用。推荐让一个模块拥有它的创建与销毁生命周期,再向其他模块提供访问函数:
import type { FlamoArtifact, FlamoInstance, FlamoRuntime } from '@flamo/runtime';
let first: FlamoInstance | undefined;
export async function mountFirst(runtime: FlamoRuntime, artifact: FlamoArtifact) { first ??= await runtime.mount('#first', artifact); return first;}
export function getFirst() { if (!first) throw new Error('first 尚未挂载'); return first;}
export async function destroyFirst() { await first?.destroy(); first = undefined;}import { getFirst } from './first-instance.js';
getFirst().set('label', 'Updated');这里导出的是对同一个活对象的引用,并没有复制或序列化 Instance。导入方也必须处理“尚未创建”和“已经销毁”的状态。
Artifact Node Script 也可以导入纯函数、hooks 和常量等普通模块代码,但不应把某个 FlamoInstance 做成模块级单例供多个 Node Script 共享。Node Script 会在每个 Instance 的作用域内挂载;共享单例会破坏多实例隔离。脚本应通过自己的 ctx / scoped flamo、组件公开的 props、events、methods 或父子实例图协作。
“Instance 不导入、不导出”真正指什么
Section titled ““Instance 不导入、不导出”真正指什么”Instance 含有 Runtime、DOM、Canvas、ECS、事件处理器和资源所有权等活引用,因此它不是可持久化或可发布的组件定义:
- 不能把 Instance 序列化到 Artifact、JSON 或存储中,再在刷新后恢复为同一个运行对象;
- 不能通过
postMessage、structured clone、Worker、iframe、浏览器标签页或服务端/客户端边界传递这个活对象; - 不能把一个已经运行的 Instance 当作可供其他项目再次实例化的组件发布物。
如果目标是跨页面、跨运行环境或跨项目复用,应导出 Artifact、组件 factory/API、exact Component Ref 或 Component Export,由接收方在自己的 Runtime 中重新 create() / mount()。要保存多个实例的组合结构,应在定义层创建 Composition IR,再生成新的 Artifact Export 与发布物,而不是保存某次运行产生的 Instance。
组合 Artifact 可以在创建时物化 runtime-owned child;也可以用 mountChild()、reconcileChildren() 和 swapChild() 临时修改当前运行图。稳定 key 与 parentKey 表达 identity 和所有权层级,mount target 决定 child 出现在 root、node、slot 或 portal 的哪个位置。
相同 key 与 identity 可按 statePolicy 复用,替换操作支持 cancellation 和 typed receipt。父实例销毁时递归释放 runtime-owned descendants;caller-owned child 只解除挂接。
临时状态与持久状态
Section titled “临时状态与持久状态”Instance 的 props、变量 override、Preview Variant 和动态 child 都是运行态状态。刷新或 remount 后默认回到 Artifact 初值。需要永久变更时,回到 Artifact/组件 authoring 路径并产生新的 revision。