Context
Context 规范
Context 是什么?
Section titled “Context 是什么?”Context 是 Proto UI 的 Component ↔ Component 信息通路。它让一组处在同一通信作用域内的组件共享一个由 ContextKey 标识的 JSON object value。
Context 是官方承认的组件间通信路径。Props、Event、Feedback、Expose 各自有明确方向,不能被扩展成组件之间的通用信息传递机制。Anatomy + Expose 可以在少数场景中作为兜底方案,但它不是 Proto UI 鼓励的首要组件间通信手段。
Context 的设计更接近“组件实例之间共享一个 scoped channel”,而不是 React 或 Vue 里单纯从 provider 往 consumer 下发数据的模型。provider 负责建立 scope 和初始值,但不垄断更新权。
ContextKey
Section titled “ContextKey”Context channel 由 ContextKey 标识。key 的 identity 按引用比较,而不是按字符串名称、debugName 或对象结构比较。
import { createContextKey } from '@proto.ui/core';
type TabsContextValue = { value: string; activeValue: string;};
export const TABS_CONTEXT = createContextKey<TabsContextValue>('base-tabs');debugName 只服务于诊断、错误信息和调试展示。两个 debugName 相同但引用不同的 ContextKey 是两个不同的 context channel。
Provide 与 Scope
Section titled “Provide 与 Scope”provider 在 setup 期间建立 context scope:
def.context.provide(TABS_CONTEXT, { value: '', activeValue: '',});
def.context.subscribe(TABS_CONTEXT);def.context.provide(key, defaultValue) 是 setup-only。它把当前组件实例注册为该 key 的 scope owner,并安装初始 context value。
同一个组件实例不能重复 provide 同一个 key。不同组件实例可以同时 provide 同一个 key;consumer 解析时绑定到当前 scope ancestry 中最近的 provider。常见宿主会用组件树或逻辑树表达 ancestry;如果宿主不是树形结构,adapter 也可以提供等价的 scope resolution 逻辑。
provide 不返回 provider 侧 update function。provider 自己需要更新 context 时,也要先在 setup 期间声明 subscription intent,再在 runtime callback 中使用 run.context.update 或 run.context.tryUpdate:
def.lifecycle.onMounted((run) => { run.context.update(TABS_CONTEXT, (prev) => ({ ...prev, activeValue: prev.value }));});Subscribe 与 Try Subscribe
Section titled “Subscribe 与 Try Subscribe”Context 订阅 API 在 setup 期间声明组件如何参与某个 context scope。
def.context.subscribe(TABS_CONTEXT, (run, next, prev) => { // required dependency: provider 必须存在});
def.context.trySubscribe(TABS_CONTEXT, (run, next, prev) => { // optional cooperation: provider 可能不存在});subscribe(key, cb?) 表示该 context 是组件成立所必需的。如果 setup 期间无法解析 provider,组件应该直接失败,因为没有这个 context 它无法完成基本交互。
trySubscribe(key, cb?) 表示该组件可以在某个 context 存在时参与协作,也可以在它不存在时自治运行。optional context 缺失不是错误;原型作者必须为缺失场景负责。
这两个 API 的分离是刻意的。它让原型作者明确表达“我一定依赖这个 context”还是“我可以尝试加入一个可能存在的协作环境”。
Runtime Read 与 Update
Section titled “Runtime Read 与 Update”Context runtime surface 遵守 setup 期间声明过的 subscription intent:
| API | 需要的 setup intent | 缺失 provider 时 |
|---|---|---|
run.context.read(key) | subscribe(key) | throw |
run.context.tryRead(key) | trySubscribe(key) | null |
run.context.update(key, next) | subscribe(key) 或 trySubscribe(key) | throw |
run.context.tryUpdate(key, next) | trySubscribe(key) | false |
runtime update 必须显式带上 ContextKey 与 next value 或 updater:
run.context.update(TABS_CONTEXT, (prev) => ({ ...prev, value: nextValue,}));这个 API 不依赖某个隐式“当前 context”。同一组件可以订阅多个 context,显式 key 能避免更新目标含糊。
Context Value
Section titled “Context Value”Context value 的顶层必须是 JSON-compatible plain object。
def.context.provide(TABS_CONTEXT, { value: '', activeValue: '',});顶层 null 不允许作为实际 context value,因为 tryRead 用 null 表示 optional context 当前不可用。嵌套值仍然必须保持 JSON-compatible:string、number、boolean、null、array 和 plain object 可以出现;function、symbol、class instance、Date、DOM object 或循环引用都不属于 portable context value。
Rebinding 与 Cleanup
Section titled “Rebinding 与 Cleanup”Context resolution 在 read/update 时按当前 scope ancestry 解析。这意味着组件树或等价 scope 关系变化后,consumer 可能重新绑定到另一个 provider,也可能进入 disconnected 状态。
v0 不提供 connected/disconnected notification API。required read/update 在 disconnected 时失败;optional tryRead/tryUpdate 用 null 或 false 表示当前不可用。
Context participation 绑定到组件实例 lifecycle。当实例 dispose 时,它提供的 context entries、创建的 subscriptions 和 pending callbacks 都必须被清理。cleanup 后旧 callback 不得继续响应新的 context update。
契约实体预览
Section titled “契约实体预览”与测试的关系
Section titled “与测试的关系”Context 覆盖映射到以下测试实体:
| 测试实体 | 主要覆盖 |
|---|---|
T-CONTEXT-0001 | ContextKey identity、provider scope、scope resolution、value boundary |
T-CONTEXT-0002 | required/optional subscription、read/update、callback delivery、render read、rebind/cleanup |
这些测试把 Context 从“组件树上的共享数据”收敛为一组可验证边界:谁建立 scope、谁能订阅、何时可读写、value 如何保持 portable,以及 scope 变化和 lifecycle cleanup 如何影响可见行为。
与其他规范的关系
Section titled “与其他规范的关系”Core定义信息通路、setup/runtime、JSON value boundary 与createContextKey。Props是 App Maker → Component 的配置输入;Context 是 Component ↔ Component 的 scoped cooperation。Expose是 Component → App Maker;通过 anatomy 取得另一个组件 expose 可以作为兜底能力,但不是官方组件间信息通路。Event是 User → Component;context 可以在 event callback 中被读取或更新,但 event 不负责组件间通信。State表达组件内部连续性;context value 可以包含状态快照,但 Context 自身不是 state slot。Lifecycle定义 provide、subscribe、runtime callback、read/update 与 cleanup 的可用边界。