DSH plugin 的 Slot 组合:cardinality、scope 与 props 推导怎么写
**DSH plugin 的 Web UI 扩展不是「导出一个组件让别人 import」,而是一套类型化组合系统:SlotMap 在编译期声明槽位,功能插件通过 ctx.slots.register() 往里贡献,组件之间从不互相 import。 这套设计有两个直接后果:props 是从声明推导出来的,不要手抄成员;注册与声明跟随 effect 生命周期,拥有者卸载时贡献会被递归折叠。理解这两条,就能避开「组件拿不到会话」「重复注册报错」「卸载后残留 UI」这三类高频问题。
DSH plugin 的 Slot 声明与生命周期
一句话结论:声明一个 child 同时做三件事,且每个声明只能有一个存活 owner。
- 声明三效果:让 child key 生效、授权父 entry 调用
renderSlot/renderSlotChain、记录运行时 dispatch 规格。 - 唯一 owner:向未声明 slot 注册,或重复声明他人已拥有的 child,都会在插件激活时失败。
- root 是特例:唯一内建声明,也是唯一由 Cordis 服务自身渲染的 key;其余后代都通过父 entry 收到的 props 渲染。
- effect 生命周期:销毁 entry 会移除贡献并递归折叠它声明的 child slot。
- 跨包贡献:用
ctx.slots.inject(key, callback),回调在每段声明生命周期内运行,owner 重挂时自动重跑。
DSH plugin 的四种 cardinality:数量与寻址
一句话结论:single 取胜者、list 按 id 并列、keyed 按 key 匹配、chain 按 select 竞争。
| cardinality | 行为 | 适用 |
|---|---|---|
single | 单个 cell,渲染当前 priority 胜者 | 有且只有一个实现的位置 |
list | 必填 id 定址,先按 order 再按注册顺序 | 并列多个同等贡献 |
keyed | owner 传 entryKey,匹配 cell 用该 key 的 props | 按实体寻址的贡献 |
chain | 每个 entry 提供 select(owner),第一个非空获选 | 需要「谁能处理」的决策 |
- 别把 single 当列表:需要并列内容时声明 child slot。
- priority 语义:single/list/keyed 里是遮蔽优先级,chain 里是选举顺序;数值越小越先。
- 新增用新 id:增量贡献用新的 list
id或 keyedkey;复用已有 cell 表示有意替换。 - chain 的 fallback:全部拒绝时渲染 owner 提供的兜底。
DSH plugin 的三种 scope:会话绑定要不要
一句话结论:root 只有一个根实例,session 要求确定会话,session-maybe 允许缺失。
- root:一个根作用域组件与 store 实例。
- session:要求可解析的外围 Provider binding,收到确定存在的 Session 值。
- session-maybe:继承外围 binding,但没有 binding 时仍渲染,Session 值可选。
- 子树的覆盖:声明 session/session-maybe child 时,
PropsRenderSlots会提供SessionProvider;不传就继承,显式传引用或 undefined 只覆盖该子树。 - 重挂规则:严格 session entry 在 binding 代际变化时重挂;空白 session-maybe entry 接受首个 binding 时不重挂,后续代际变化或回到缺失才重挂。
DSH plugin 的组件 props:从声明推导,不要抄写
一句话结论:六类输入各有声明者,组件从类型推导 props,服务对象留在闭包里。
| 输入 | 来源 | 类型 |
|---|---|---|
| owner 值与 scope 值 | SlotMap 条目与 scope adapter | PropsRuntime<K> |
| 获授权的 child renderer | 注册项的 children keys | PropsRenderSlots<S> |
| 共享视图状态 | 注册项的 store | PropsStore<H> |
| 私有数据与 observable | 注册项的 inject factory | InjectFace<I> |
| 本地化函数 | locale namespace | PropsLocale<N> |
| chain 选中的值 | select 结果 | ComposedProps.matched |
- 没有 ctx:组件永远收不到上下文对象。
- 父值走 owner 参数:父组件渲染时已知的值通过
renderSlot的 owner 参数进入。 - 服务不暴露:service 与 model object 留在
apply闭包里,只投影 callback 或 observable source。 - 本地化:用声明的
localenamespace,不要硬编码文案。
DSH plugin 的 Slot 排查注意事项
一句话结论:Slot 问题先分「没声明」「注册冲突」「拿不到值」三类。
- 注册报错 → 检查目标 slot 是否已声明、child 是否已被他人拥有。
- 重复注册 → 同一
id第二次注册会抛错,检查是否在两个地方各注册了一次。 - 拿不到会话 → 检查 scope 是 session 还是 session-maybe,以及外层是否有 binding。
- 卸载后还在 → 注册没挂在 effect 上,或用了 inject 却没走回调。
- 列表顺序不对 → 检查
order,再看注册顺序。 - 修复状态如实:本文的声明效果、四种 cardinality、三种 scope 与六类输入均取自官方「Web Client Slots」页面;具体 slot key 与类型以官方包与你的版本为准。
常见问题
DSH plugin 的注册与声明跟随 Cordis effect 生命周期:拥有者折叠时贡献会被移除。用 slots.inject 的回调会在每段声明生命周期内重新运行,拥有者重挂时贡献自动回来,不需要你手动补注册。
DSH plugin 的 single 只渲染当前优先级胜者,list 用必填 id 并列,keyed 由 owner 传 entryKey 匹配,chain 让每个 entry 用 select 竞争唯一结果。要并列多个内容就声明 child slot,不要把 single 当列表用。
DSH plugin 的 session 要求可解析的外围 Provider binding,并拿到确定存在的会话值;session-maybe 没有 binding 时仍可渲染,会话值是可选的。选错会让组件在缺少会话时直接不渲染。
不能,DSH plugin 的 slot 组件绝不会收到 ctx。父组件已知的值经 renderSlot 的 owner 参数进入,共享视图状态用声明的 store,服务与模型对象留在 apply 闭包里,只向组件投影 callback 或 observable。
DSH plugin 声明 child 会同时做三件事:让 child key 生效、授权父 entry 调用 renderSlot 或 renderSlotChain、记录运行时 dispatch 规格。每个声明只能有一个存活 owner,重复声明会在激活时失败。
相关术语
- SlotMap
- SlotMap 是 DSH plugin 的 Web UI 编译期注册表,包通过声明合并写入 key、cardinality、scope、owner props、keyed props 与可选的 inject face。运行时声明则是拥有组件在 children 中给出的条目。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- cardinality(基数)
- cardinality 决定一个 slot 能容纳几个贡献,取值为 single、list、keyed 或 chain。它只描述数量与寻址方式,与 scope 是两个独立维度。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- scope(作用域)
- scope 决定 slot 组件能否拿到会话绑定,取值为 root、session-maybe 或 session。root 只有一个根实例,session 要求确定的会话值,session-maybe 允许缺失。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
- chain cardinality
- chain 是 DSH plugin 的一种 slot 基数:每个 entry 提供纯 select(owner) 函数,按优先级遇到的第一个非空结果获选并以 matched 传给组件,全部拒绝时渲染 owner 的 fallback。— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/slots
来源
- DeepSeek Harness 文档 — Web Client Slots(声明、cardinality、scope、组件输入、扩展规则)· deepseek-harness.github.io
- dsh-client-ui-slots 包 README(注册表与类型代数)· deepseek-ai(GitHub)
- DeepSeek Harness 文档 — 客户端资源(slot 组件拿到 useResource)· deepseek-harness.github.io