Core API 参考
本文示例使用以下公共入口:
import { DisposableBag, RenderPage, createAppContext, createAppContextKey,} from 'ds-ui'
Core API 是框架最底层的能力集合,包括 render tree、布局约束、焦点、输入、剪贴板、Popup、上下文、命令、状态绑定和资源释放。业务开发通常优先使用 Widgets API 和 Layout API;只有开发可复用组件、诊断工具或框架扩展时,才需要直接接触 Core API。
导出清单
状态和生命周期:
DisposableDisposeFnDisposableBagBindingBagbindSelectorScheduleFnScheduleModecreateStoreStore
Render tree 和布局协议:
RenderObjectPipelineOwnerBoxConstraintsLayoutContextLayoutPassKindRenderLifecycleAwareRenderLoadedContextOffsetRectSizeconstrainSizetightConstraintslooseConstraints
焦点和输入:
FocusManagerFocusScopeFocusScopeTypeFocusableInputComposerInputSessionInputSessionToken
剪贴板:
ClipboardControllerClipboardTextWriterCopyableSelectionisCopyShortcutisCopyableSelection
App Context 和命令:
APP_CONTEXT_PROVIDERAppContextKeyAppContextRegistryAppContextInitialValuesAppContextLookupKeyAppContextProviderAppContextSetOptionscreateAppContextcreateAppContextKeyisAppContextProviderCOMMAND_SCOPE_PROVIDERCommandManagerCommandScopeCommandShortcutControllerallowAllPermissionServicecommandManagerContextKeyisCommandScopeProvider
Popup、锚点和滚动视口:
PopupManagerPopupPopupAnchorModePopupContextPopupContextProviderPopupOpenOptionsPopupPaintInvalidatorPopupViewportOverlayInvalidatorOverlayPaintInvalidatorGET_POPUP_ANCHOR_RECTPopupAnchorPopupAnchorPlacementPopupAnchorTargetresolvePopupAnchorRectresolvePopupAnchorTargetScrollViewportScrollViewportClientisScrollViewportClient
事件和命中测试:
HitTestResultDispatchPhaseDispatchEventHitTestEntryHitTestTargetPointerEventWheelPointerEventEventDispatcherInteractiveRenderObjectPointerDispatchDebugSnapshotisPrimaryPointerButton
绘制:
PaintContext
RenderObject
RenderObject 是所有可布局、可绘制、可命中的对象基类。业务组件通常继承 RenderBox 或已有 widget;只有实现底层渲染协议时才直接继承 RenderObject。
常用职责:
- 持有
parent、owner、offset、size。 - 参与 attach / detach / dispose 生命周期。
- 通过
markNeedsLayout()、markNeedsPaint()、markNeedsTransientPaint()请求管线刷新。 - 通过
paintBounds声明包含阴影等视觉溢出的绘制区域;仅移动节点时用markNeedsPaintForGeometryChange()累计旧、新损伤区域。 - 通过
visible控制折叠隐藏;不可见时尺寸为 0,不参与 layout、paint、hit test 和焦点。 - 通过
hitTest()、hitTestPath()参与事件命中。 - 通过
debugInfo()暴露布局检查信息。 - 通过
getAppContext()/requireAppContext()读取当前 render tree 的上下文。
自定义组件原则:
- 在属性 setter 中判断值是否变化,再标记 layout 或 paint。
- layout 外修改
offset时先保存旧paintBounds,再调用markNeedsPaintForGeometryChange()。 - 不要在
performPaint()中修改布局相关状态。 - 维护好子节点的
parent,替换子节点时处理 detach。 dispose()中释放订阅、定时器、popup、输入会话和外部资源。
PipelineOwner
PipelineOwner 管理 layout、paint、transient paint 和 app context。
常见调用方是 runtime,不是业务页面。组件应通过自身的 markNeedsLayout() / markNeedsPaint() 进入管线,不要直接操作 pipeline 内部队列。
相关概念:
| 概念 | 说明 |
|---|---|
| layout | 重新计算尺寸和 offset。 |
| paint | 重新绘制稳定图层内容。 |
| transient paint | 光标闪烁、临时 overlay 等不应触发完整 layout 的轻量绘制。 |
| app context | 当前 runtime 根上下文。 |
约束和几何类型
BoxConstraints:
| 字段 | 类型 | 说明 |
|---|---|---|
minWidth |
number |
最小宽度。 |
maxWidth |
number |
最大宽度。 |
minHeight |
number |
最小高度。 |
maxHeight |
number |
最大高度。 |
几何类型:
| 类型 | 字段 |
|---|---|
Offset |
{ x: number; y: number } |
Size |
{ width: number; height: number } |
Rect |
{ x: number; y: number; width: number; height: number } |
工具函数:
| 函数 | 说明 |
|---|---|
constrainSize(constraints, size) |
把自然尺寸限制到约束范围内。 |
tightConstraints(width, height) |
生成固定尺寸约束。 |
looseConstraints(width, height) |
生成最小为 0、最大为指定宽高的约束。 |
FocusManager
焦点系统负责当前键盘和输入目标。
核心导出:
FocusManagerFocusScopeFocusScopeTypeFocusable
Focusable 对象通常实现:
focusIn()focusOut()onKeyDown(event)
焦点规则:
- Popup 打开时会建立 popup focus scope。
- 最后一个交互 popup 关闭时恢复之前焦点。
- 组件 dispose 时应从焦点系统注销,避免旧对象继续接收键盘事件。
业务页面通常不直接操作 FocusManager;输入控件、窗口、popup 和 runtime 会处理大部分焦点流转。
InputComposer
InputComposer 是隐藏输入承接器,负责键盘输入、中文 composition、粘贴和文本输入会话。
核心导出:
InputComposerInputSessionInputSessionToken
使用边界:
- 隐藏输入控件只承接当前输入,不应保存整份大文档。
- 大文本粘贴应从 paste event 读取文本后直接写入文档模型,并阻止默认落入 textarea。
- composition update 可以更新预编辑文本;composition end 再提交最终文本。
- 页面或组件销毁时要结束输入会话。
普通输入组件和文本编辑器已经封装输入会话,业务页面不应直接访问隐藏输入控件。
ClipboardController
剪贴板系统统一处理复制快捷键和可复制选区。
核心导出:
ClipboardControllerClipboardTextWriterCopyableSelectionisCopyShortcutisCopyableSelection
CopyableSelection 组件应提供 getCopyText(),返回当前选区对应文本。TreeView、GridView、ObjectInspector、MarkdownViewer、PlainTextEditor 都应复用这套语义。
使用边界:
- 组件只负责提供文本,不直接调用浏览器剪贴板 API。
- 剪贴板写入失败要允许业务或运行时降级处理。
- 调试类组件复制对象时,应复制可读的 key path 和完整值摘要。
App Context
App Context 用于按范围传递依赖对象。它的详细 API 见 App Context API。
最小示例:
const coreNameKey = createAppContextKey<string>('currentName')const coreContext = createAppContext()coreContext.set(coreNameKey, '示例')const coreName = coreContext.require(coreNameKey)coreName.length
关键边界:
- key 定义在稳定模块,读取端和写入端复用同一个 key。
- 页面级对象放页面 scope,不放应用根。
- controller、订阅和定时器使用
setDisposable()或自定义 disposer。
Commands
命令系统统一按钮、菜单、快捷键、权限和启用状态。详细 API 见 Commands API。
核心对象:
CommandManagerCommandScopeCommandShortcutControllerRenderCommandScopeRenderCommandButtonRenderCommandToolbar
关键边界:
- 命令是业务动作,不是按钮。
- 页面命令注册在页面 scope,随页面释放。
canExecute是轻量同步求值,不做网络请求和重计算。- 状态变化后调用
commandManager.invalidate()。
Popup 和 OverlayInvalidator
Popup 底层能力包括:
PopupManagerPopupPopupAnchorModePopupContextPopupContextProviderPopupOpenOptionsOverlayInvalidatorGET_POPUP_ANCHOR_RECTresolvePopupAnchorRectresolvePopupAnchorTarget
详细说明见 Popup 弹出层。
关键边界:
- 普通业务优先使用 Dropdown、DatePicker、Popover、Modal、Drawer 等上层组件。
- 自定义 popup 要处理 hit test、外部点击、Escape、滚轮、focus 和 close 释放。
- 锚点位置应动态读取,不能只在打开时缓存一次。
- 页面销毁时关闭 owner 关联 popup。
ScrollViewportClient
ScrollViewportClient 用于让外部滚动容器把可视区域同步给子组件。Masonry、虚拟列表、文档查看器等可以据此减少不可见区域计算。
导出:
ScrollViewportScrollViewportClientisScrollViewportClient
如果组件实现了 setScrollViewport(viewport),RenderScrollViewer 会在 layout 和滚动时同步可视信息。
EventDispatcher 和命中测试
事件系统负责从 canvas 事件到 render tree 的路由。
核心导出:
EventDispatcherHitTestResultDispatchPhasePointerEventWheelPointerEventInteractiveRenderObjectisPrimaryPointerButton
业务组件实现复杂交互时应通过 hitTestPath()、onPointerDown()、onPointerMove()、onWheel() 等协议接入,不要直接监听全局 DOM 事件。
DisposableBag
DisposableBag 用于集中释放资源。
适合注册:
- 事件监听。
- store 订阅。
- popup / overlay close 句柄。
- 定时器释放函数。
- 业务 controller。
页面、弹窗、控制器和复杂组件都应在 dispose() 中释放自己的 DisposableBag。
class AutoRefreshPage extends RenderPage { private readonly disposables = new DisposableBag() constructor() { super() this.disposables.setInterval(() => { reload() }, 30000) } override dispose(): void { this.disposables.dispose() super.dispose() }}
如果资源生命周期跟页面上下文一致,也可以把 DisposableBag 通过 AppContextRegistry.setDisposable() 托管给页面 context。
BindingBag 和 Store
createStore() / Store 提供轻量状态容器,BindingBag / bindSelector() 提供把状态投影绑定到组件属性的能力。
适合场景:
- demo 和轻量页面状态。
- UI 局部状态同步。
- 将 store selector 的变化调度到组件更新。
复杂业务系统可以接入自己的状态管理方案。框架不会强制业务状态必须放入 Store。
PaintContext
PaintContext 是绘制上下文封装,组件 paint 阶段通过它访问 canvas 2D context、主题和绘制辅助能力。
业务页面通常不直接使用 PaintContext。只有开发自绘组件、图表、条码、编辑器或文档渲染器时才需要关注。
使用边界
- 业务页面优先使用 widgets 和 layout API。
- 自定义通用组件时再深入 core API。
- 不要依赖未从包入口导出的内部 helper。
- 不要在业务层直接操作 render tree 私有字段。
- 不要在 paint 中触发 layout 或业务副作用。
- 管线调度、频繁重绘、焦点异常优先用运行时诊断定位。