集成外部工具
ElfUI 不需要为每个图表、编辑器、renderer、overlay、Observer、Worker 或 WASM 包内置 adapter。把工具视为外部资源的拥有者,并把资源所有权绑定到组件生命周期即可。
基本契约
- setup 阶段创建 template ref。
- 在
onMounted初始化工具,此时最终 DOM 与 ref 已就绪。 - 响应式数据变化通过工具自身的 update API 传入。
- 卸载时停止 Observer、监听器、timer、Worker、portal 和工具实例。
- 让异步任务失效,避免旧任务完成后写入已经断开的组件。
ts
import {
defineHtml,
onMounted,
useTemplateRef,
} from "@elfui/core";
const canvas = useTemplateRef<HTMLCanvasElement>("canvas");
onMounted(async () => {
const { createRenderer } = await import("some-dom-tool");
if (!canvas.value?.isConnected) return;
const instance = createRenderer(canvas.value);
return () => instance.destroy();
});
export default defineHtml(`<canvas ref="canvas"></canvas>`);示例包名只用于说明模式,ElfUI 不会打包它。
跟踪尺寸与可见性
Observer 组合式函数接受 element、ref 或响应式 getter。应传入 ref 本身,让观察在 mounted 后开始并跟随目标替换:
ts
const canvas = useTemplateRef<HTMLCanvasElement>("canvas");
useResizeObserver(canvas, ({ width, height }) => {
renderer?.resize(width, height);
});
useIntersectionObserver(canvas, ({ isIntersecting }) => {
isIntersecting ? renderer?.resume() : renderer?.pause();
});两个 helper 都会随所属组件断开,并忽略旧目标的延迟回调。浏览器没有对应 Observer API 时会安全地保持未启用状态。
资源所有权规则
| 资源 | 创建位置 | 释放方式 |
|---|---|---|
| 图表、编辑器、renderer 实例 | onMounted | 返回工具的 destroy() 清理函数 |
| 全局 DOM 监听器 | useEventListener 或 mounted | helper 自动清理或 removeEventListener |
| Resize/Intersection Observer | Observer 组合式函数 | 自动 |
| Portal/overlay root | onMounted | 移除监听器与空 portal 节点 |
| Worker | onMounted 或所属 effect | 忽略旧消息,再调用 terminate() |
| Timer/animation frame | mounted 或所属 effect | clearTimeout / cancelAnimationFrame |
| 异步 import/request/WASM setup | mounted generation/token | teardown 前失效或 abort |
同一轮事件循环内的同步 DOM move 会保留组件实例。真正断开时资源会释放,稍后重连会创建新实例并再次执行 onMounted。异步 mounted 钩子在卸载后才返回清理函数时,ElfUI 会立即执行它。KeepAlive 失活不等于卸载:在 onDeactivated 暂停昂贵工作,在 onActivated 恢复,最终通过 mounted 清理函数或 onUnmounted 销毁。
发布门禁覆盖
ElfUI 集成套件会在真实 Chromium 中运行 DOM 接管、Canvas 图表、SVG/WebGL、Shadow DOM overlay/portal、原生 Observer/全局监听器、Worker/WASM 取消、异步竞态、分支和 keyed list 释放、KeepAlive 淘汰、App unmount,以及 100 轮重复挂载/卸载。这些包只属于测试用开发依赖,不会进入发布包。
这套测试验证的是框架生命周期与平台边界;应用仍应针对自己选用的外部工具版本和配置运行集成测试。
