DirectSurface UIDirectSurface UI
开始使用
文档/核心概念

生命周期与释放

本文完整示例使用以下公共导入:

import { DisposableBag, RenderPage } from 'ds-ui'

DirectSurface UI 中的组件可能持有子组件、事件订阅、定时器、缓存、输入会话、弹窗和业务上下文。生命周期管理不完整会导致内存泄露和异常重绘。

常见生命周期

组件通常经历:

  1. 创建对象。
  2. attach 到渲染树。
  3. layout。
  4. paint。
  5. 处理交互。
  6. detach 或 dispose。

attach / detach 代表组件是否连接到当前渲染管线。dispose 代表组件不再使用,应释放资源。

dispose 职责

组件在 dispose() 中应释放自己持有的资源。页面或组件里如果创建了定时器、订阅、事件监听、动画帧或页面级 controller,推荐统一放进 DisposableBag,再在 dispose() 中释放:

class PatientPage extends RenderPage {  private readonly disposables = new DisposableBag()   constructor() {    super()     this.disposables.setInterval(() => {      this.refresh()    }, 30000)  }   override dispose(): void {    this.disposables.dispose()    super.dispose()  }   private refresh(): void {    void this.loadLatestData()  }   private async loadLatestData(): Promise<void> {    // 调用业务服务刷新当前页面数据。  }}

这个模式等价于给页面或组件准备一个轻量资源容器。谁创建定时器或订阅,谁把释放函数放进 DisposableBag;谁拥有页面生命周期,谁在 dispose() 中释放这个 bag。

典型资源:

  • 子组件。
  • DisposableBag
  • 事件订阅。
  • 定时器。
  • 动画控制器。
  • Popup。
  • 输入会话。
  • 图片或文档缓存。
  • 页面私有 AppContext。

页面关闭

Tab、窗口或业务页面关闭时,应释放页面级资源。页面私有上下文也应随页面关闭销毁,避免浮动窗口或弹窗继续持有旧业务对象。

如果一个组件接收外部传入的 DOM 或 canvas,销毁时不能擅自移除外部拥有的 DOM,只能清理自己创建和持有的资源。

常见泄露来源

  • 全局缓存持有文档对象。
  • Popup 关闭后还引用页面组件。
  • 定时器未清理。
  • 事件订阅未取消。
  • 输入 composer 会话未结束。
  • 页面关闭后仍在请求重绘。
  • 动画 controller 持续通知。

检查方式

可以结合 Runtime Diagnostics、浏览器 heap snapshot 和页面销毁按钮做检查:

  • 打开复杂页面。
  • 加载大数据或大文档。
  • 关闭页面并手动销毁应用实例。
  • 生成 heap snapshot。
  • 查找仍被持有的页面、文档、canvas、popup 和缓存对象。

原则

  • 谁创建,谁释放。
  • 谁注册,谁取消。
  • 页面私有对象跟随页面生命周期。
  • 全局对象只能缓存真正跨页面共享且可控的资源。