执行语义
执行模型与语义
这篇文章要回答什么?
Section titled “这篇文章要回答什么?”前面的章节已经讨论了:
- 组件为什么可以被当作协议来理解
- Proto UI 如何通过信息通路来识别组件的核心关系
- 一个子结构什么时候应当被视为独立原型
接下来的问题是:
这些被识别出来的原型,究竟如何在时间中成立?
如果原型只是静态结构,那么它最多只能描述“组件包含什么”;
但组件作为交互主体,真正成立的方式并不是静止的。
它会进入宿主、经历阶段变化、开启能力、响应输入、产生反馈,也会在适当的时候退出。
因此,这一篇要回答的是:
- Proto UI 的原型为什么不是静态描述
- 为什么 Proto UI 必须区分
setup与runtime - 为什么生命周期不是附属能力,而是交互语义成立的一部分
- Proto UI 所说的一致性,究竟是在什么意义上成立
原型不是静态结构
Section titled “原型不是静态结构”Proto UI 中的原型,并不只是“组件长什么样”的结构说明。
它描述的是一个会在时间中展开的交互主体。
这个主体并不是在任何时刻都拥有同样的能力,也不是在任何时期都承担同样的职责。
有些事情只应当发生在定义阶段,例如:
- 声明它会响应哪些交互
- 声明它有哪些状态与反馈
- 规划它会暴露哪些能力
另一些事情则只能发生在真正运行时,例如:
- 读取当前状态
- 响应一次具体输入
- 触发副作用
- 在上下文变化中调整行为
如果这些内容都被混在同一个层面,原型就会变成一种既在描述自己、又在临时执行自己的混合体。
这种混合在局部实现里也许可以工作,但它很难成为一种稳定的协议对象。
因此,对 Proto UI 来说,执行语义并不是附加细节,而是原型成立的必要条件。
没有执行语义,原型就只有结构;
有了执行语义,原型才真正成为一个会在时间中成立的交互主体。
为什么必须区分 setup 与 runtime?
Section titled “为什么必须区分 setup 与 runtime?”Proto UI 对原型的执行过程,首先作出一个基础切分:
setupruntime
setup 是定义、计划和声明交互行为的时期。
在这个时期里,原型还没有真正进入一次具体运行。
它更像是在说明:
- 这个交互主体会具备哪些能力
- 它将如何组织自己的通路和状态
- 它准备在后续运行中遵守哪些规则
setup 的重点不在“执行一次交互”,而在“把交互主体定义清楚”。
runtime
Section titled “runtime”runtime 是原型真正进入宿主、开始承担具体交互责任的时期。
在这个时期里,组件已经不再只是一个被描述的对象,
而是一个正在运行的对象。它会:
- 接收输入
- 读取状态
- 参与上下文变化
- 产生反馈
- 触发副作用
- 在生命周期推进中表现出不同能力
从这个角度看,runtime 不是 setup 的附属阶段,
而是原型真正作为交互主体发生作用的时期。
这种分期为什么重要?
Section titled “这种分期为什么重要?”setup 与 runtime 的区分,首先是在避免一种很常见的混杂:
把描述行为和执行行为写在同一个层面里。
一旦这种混杂出现,原型会很快失去几个关键特征:
- 可理解性:读者很难判断某段内容是在声明能力,还是在消耗能力
- 可翻译性:适配器无法稳定地区分哪些是要被解释的定义,哪些是实际运行中的操作
- 可验证性:语义边界不清,很多一致性要求就会退化成“实现自己尽量对齐”
Proto UI 之所以强调执行时期,不是为了把组件写得更复杂,
而是为了把这些边界提前固定下来。
只有这样,原型才不会变成一段“宿主大概能猜出意图”的实现脚本,
而能够保持为一份可翻译、可约束的交互定义。
生命周期在这里扮演什么角色?
Section titled “生命周期在这里扮演什么角色?”setup / runtime 先区分了定义期与运行期,
但这还不够。
一旦进入 runtime,组件仍然不会在所有时刻都处于同一种状态。
它仍然需要面对:
- 什么时候开始成立
- 什么时候某些能力已经开启
- 什么时候某些能力仍然有效
- 什么时候应当释放责任并退出当前关系
这正是生命周期要组织的内容。
从 Proto UI 的角度看,生命周期并不是附加在原型旁边的一组便利机制,
而是在时间维度上继续组织原型能力边界的方式。
如果没有生命周期,runtime 就只是一个过于宽泛的时期;
而有了生命周期,原型才真正拥有了更细的时间秩序。
setup 与生命周期并不是一回事
Section titled “setup 与生命周期并不是一回事”这里有一个容易混淆的地方。
setup / runtime 处理的是更粗粒度的执行分期;
生命周期处理的则是 runtime 内部的阶段推进与能力边界。
也就是说:
setup/runtime区分“定义”和“运行”- 生命周期继续区分“运行中的不同阶段”
两者不是替代关系,而是不同层级的组织方式。
如果没有 setup / runtime 的切分,
生命周期容易退化成实现层面的零散回调约定;
如果没有生命周期,
runtime 又会变成一个内部缺乏秩序的宽泛时期。
Proto UI 同时需要这两层,
才能让原型在时间中保持清晰边界。
执行语义不只是在谈生命周期
Section titled “执行语义不只是在谈生命周期”虽然生命周期是执行语义的核心部分,但它并不是全部。
执行语义真正关心的是:
同一个原型,在不同适配结果中,哪些交互语义必须保持一致,以及这种一致应当严格到什么程度。
这里至少会涉及几类典型问题。
feedback 的一致性
Section titled “feedback 的一致性”一个原型进入不同宿主之后,反馈不应当只是“差不多有这个意思”。
尤其当这些宿主共享相近的能力基础时,Proto UI 对一致性的要求并不宽松。
例如,同样适配到 Web 技术中的不同宿主(如 React、Vue、Web Components),
它们在 feedback 通路上拥有近似相同的潜力。
这意味着 Proto UI 不会把“看起来差不多”视为足够。
在这种情况下,一致性的目标会尽可能趋近于:
- 相同的视觉结构
- 相同的反馈行为
- 极小的还原差异
在理想情况下,它们甚至应当接近分毫不差。
最差也应当接近所谓的“像素级一致”。
换句话说,Proto UI 对一致性的要求并不是天然宽松的。
当宿主共享足够多的承载条件时,它会要求非常严格的还原度。
event 的一致性
Section titled “event 的一致性”event 通路中的一致性,更多体现为交互形态的一致性。
例如,同样一个“点击”或“长按”相关的交互,
在不同宿主中可能会存在非常细微的触发差异:
- 触发时机略有不同
- 判定条件略有不同
- 某些交互媒介下的行为边界略有不同
这些差异并不一定会破坏原型,
但也不能被简单地视为“反正都差不多”。
Proto UI 在这里关心的,是:
同一个交互语义,在不同宿主中是否仍然被还原为同一类交互责任。
如果差异已经大到改变了交互的性质,
那就不再是单纯的宿主差异,而是原型语义本身发生了漂移。
lifecycle 与能力开启的一致性
Section titled “lifecycle 与能力开启的一致性”生命周期相关的一致性,则体现在:
- 什么时候某些能力已经成立
- 什么时候某些能力仍然有效
- 某些交互与反馈应当在什么阶段发生
如果这些边界在不同宿主中被随意处理,
那么同一个原型即使看起来“功能都在”,也可能在时序上完全不是同一个东西。
因此,生命周期的一致性不是附加要求,
而是 Proto UI 保持语义连续性的基础条件之一。
Proto UI 的一致性要求并不总是同样严格
Section titled “Proto UI 的一致性要求并不总是同样严格”看到这里,另一个误解也需要被排除:
Proto UI 是否要求所有宿主中的表现都尽可能完全相同?
答案并不是简单的是或否,
而是要看这些宿主共享多少能力前提,以及原型自身如何定义差异边界。
在共同依赖较强的宿主之间,一致性要求更严格
Section titled “在共同依赖较强的宿主之间,一致性要求更严格”如果两个宿主本质上建立在相近的能力基础上,
那么 Proto UI 会对它们提出更高的一致性要求。
例如,同为 Web 技术体系中的宿主,它们共享的能力前提非常多。
这时 Proto UI 理论上会希望它们:
- 生成几乎一致的 DOM 结构
- 呈现几乎一致的视觉结果
- 拥有极其接近的交互与时序表现
这里的要求不是“风格差不多就行”,
而是尽量维持高保真的同构还原。
在跨平台场景中,一致性由原型规则决定
Section titled “在跨平台场景中,一致性由原型规则决定”但如果宿主之间的交互媒介本身已经发生变化,
Proto UI 所要求的一致性,就不再等于“表现形式必须长得一样”。
例如,一个在桌面端以“下拉框”方式呈现的交互,
在移动端未必仍然要坚持相同的呈现方式。
在许多场景下,以抽屉式 picker 呈现反而更自然。
这里关键的问题不是“视觉上有没有完全一样”,
而是:
这种差异是否仍然在原型允许的语义边界之内。
Proto UI 不会自动要求:
- PC 上是下拉框
- 移动端就也必须还是下拉框
相反,这类差异应当由原型自身来规定。
因为 Proto UI 的原型并不只描述静态输出,
它也可以描述:
- 对不同交互媒介的专属事件处理
- 对不同宿主条件下的差异化逻辑
- 哪些变化属于允许的宿主化表达,哪些变化会破坏交互主体本身
因此,跨平台场景中的一致性不是“形式完全相同”,
而是“在原型规则允许的范围内,保持同一交互主体”。
执行语义真正约束的是什么?
Section titled “执行语义真正约束的是什么?”把这些内容放在一起看,Proto UI 的执行语义实际上约束的是三件事:
- 何时定义,何时执行
- 何时成立,何时可用,何时释放
- 同一个原型在不同宿主中,哪些语义必须保持一致,哪些差异可以被原型吸收
这也是为什么执行语义不只是运行时细节。
它决定的不是“实现写起来顺不顺手”,
而是:
同一个原型在不同适配结果中,是否仍然能被视为同一个交互主体。
没有这层约束,Proto UI 仍然可以写出一套原型语法;
但它很难把这些原型维持为真正的协议对象。
这一篇没有展开什么?
Section titled “这一篇没有展开什么?”为了保持主线清晰,这一篇不会继续展开:
- 完整的时期 API 清单
- 适配器生命周期的具体门控点
- 工程层面的运行时架构
- 各宿主如何逐项实现这些语义约束
这些内容会在后续章节或工程文档中继续讨论。
如果执行语义已经成立,那么接下来的问题就是:
这些被约束好的原型语义,究竟如何进入具体宿主,并在那里被实现出来?
这正是下一篇 翻译层:Adapter / Compiler 要讨论的内容。