Template
Template 规范
Template 是什么?
Section titled “Template 是什么?”Template 是 render function 产出的结构描述。它告诉 runtime/adapter:当前原型实例的 Root Node 内部应该有哪些 children,这些 children 如何嵌套,以及哪些局部结构只携带静态视觉反馈。
Template 不是组件实例,也不是 Root Node。Root Node 由 runtime、adapter 或 host 创建,并承载组件级通路;template 返回的最外层节点只是 Root Node 内部的 child。这个边界很重要:原型的 props、event、feedback、expose、context 等组件级能力不挂在普通 Template Node 上。
Root Node 与 TemplateChildren
Section titled “Root Node 与 TemplateChildren”render function 返回的是 TemplateChildren:
return (r) => [r.el('span', 'Label'), r.el('span', { style: tw('opacity-50') }, 'Hint')];这个数组会被展开为同一个 Root Node 内部的 sibling children。它不会声明两个组件 root,也不会把第一个 span 提升为 Root Node。
Root Node 是组件级通路的锚点。adapter 可以把它实现为 DOM element、framework component root、native host node 或其他宿主容器,但 template 层只描述其内部 children。
Template Node
Section titled “Template Node”Template Node 是结构节点。它可以拥有 children、参与树结构组合,也可以携带 style 表达:
r.el('div', { style: tw('flex flex-col gap-2') }, [ r.el('span', 'Title'), r.el('span', { style: tw('text-muted-foreground') }, 'Description'),]);v0 中,普通 Template Node 不直接承载 props、state、event、expose 或 feedback 的组件级通路能力。它只是在 Root Node 内部组织结构。style 是一个特例:它激活的是纯静态 feedback-only 能力,语义由 feedback.style 定义。
Renderer el()
Section titled “Renderer el()”el() 是 template authoring 的基础构造器:
r.el(type);r.el(type, children);r.el(type, props);r.el(type, props, children);第二个参数的分派规则是固定的:如果它是合法 TemplateProps,就被解释为 props;否则被解释为 children。三参数形式中,第二参数必须是合法 TemplateProps。
v0 TemplateProps 只允许空对象或 { style?: TemplateStyleHandle }。这意味着 template 里不能通过 props 参数偷偷塞入 host attrs、event handlers、prototype refs 或其他组件级能力。
Children 归一化
Section titled “Children 归一化”Template children 会被归一化为 portable shape。默认策略是 deep flatten,并将 null 视为空。
['a', [null, ['b']]] -> ['a', 'b']顶层 undefined 会归一为 null;数组内部的 null 会被移除;全空结果归一为 null。boolean child 和 child 位置的 undefined 会抛错,因为它们在不同 host 中容易产生不一致语义。
归一化只负责 children shape,不负责证明任意 object child 都是合法 TemplateNode。后续 adapter 或 renderer 边界仍可以对节点类型做更具体的校验。
Slot Protocol
Section titled “Slot Protocol”v0 slot 是 template 层的 reserved node,表示外部 children 的插入点:
return (r) => [r.el('button', [r.slot()])];slot 必须匿名、唯一、无参数。它不携带 name、props、params、style、children 或 fallback。更复杂的 named slot、scoped slot 或 fallback slot 属于上层框架或编译层,不属于 core template protocol。
adapter 可以依赖这个事实进行宿主映射:一个 render 输出中最多只有一个 slot,且 slot 不需要参与额外参数解析。
不提供 Prototype 级组合
Section titled “不提供 Prototype 级组合”Proto UI v0 不提供原型级 template composition。正常 authoring path 不应该产生 PrototypeRef template type;如果官方 adapter 或 compiler 在 template 中遇到 PrototypeRef,必须主动抛错,而不是悄悄支持。
这个拒绝更多是合规信号:adapter 不应私自扩展 core template protocol,把另一个原型当作普通 template node 嵌进去。需要独立 props、event、expose、context 或 state 关系的子结构,应拆成独立原型,并通过宿主或上层组合机制组织。
契约实体预览
Section titled “契约实体预览”与测试的关系
Section titled “与测试的关系”Template 的测试映射集中在 spec/tests/T-TEMPLATE-0001.yaml,并由 core template 测试与 Web Component adapter boundary 测试共同覆盖。
| 测试实体 | 主要覆盖 |
|---|---|
T-TEMPLATE-0001 | root children、node structure、el() dispatch、normalize、slot、PrototypeRef rejection |
这些测试把 template 从“某种 host 的渲染树”收敛为跨 host 可验证的结构协议。
与其他规范的关系
Section titled “与其他规范的关系”Core定义 setup 返回 render function、render 返回TemplateChildren,Template 细化这些 children 的结构协议。Lifecycle决定 render 何时发生、commit 何时完成;Template 只描述 render 产物。Feedback定义 templatestyle的 style token 语义;template 自身不定义完整样式系统。Props、Event、Expose、Context、State等组件级通路由 Root Node 和对应模块承载,不由普通 Template Node 直接承载。Prototype Boundary解释何时应拆分原型;Template 只保留 feedback-only 局部结构不强制拆分的结构空间。