翻译层:Adapter / Compiler
适配器与编译器
这篇文章要回答什么?
Section titled “这篇文章要回答什么?”前面的章节已经讨论了:
- 组件为什么可以被当作协议来理解
- Proto UI 如何通过信息通路来识别组件的核心关系
- 原型边界为什么不能随意切分
- 原型的语义为什么必须在执行过程中保持稳定
接下来的问题是:
这些已经被定义、约束并组织好的原型,如何进入具体宿主?
Proto UI 的原型并不是某个宿主中的最终实现。
它描述的是一个更高层的交互对象,而宿主只接受自己能够承载的具体产物。
因此,这一篇要讨论的是:
- 为什么 Proto UI 的原型不能直接等同于宿主实现
- 为什么原型需要通过翻译层才能落地
Adapter与Compiler在这里分别意味着什么- 宿主边界会如何影响原型的还原程度
原型不是宿主实现的别名
Section titled “原型不是宿主实现的别名”Proto UI 中的原型,描述的是组件的交互语义。
它回答的是:
- 这个组件如何与外部交换信息
- 它在什么边界上成立
- 它在时间中如何展开
- 哪些交互语义需要被维持
宿主真正承载的,则是另一类东西:
- 框架组件
- 平台控件
- DOM 结构
- 渲染树
- 宿主事件系统中的处理逻辑
- 符合宿主约束的状态与副作用组织方式
原型与宿主实现之间,并不是“同一对象换一种语法”,
而是两个层次不同的对象。
Proto UI 需要让原型先停留在交互语义这一层,
再通过中间层进入具体宿主。
这一步就是翻译层。
为什么需要翻译层?
Section titled “为什么需要翻译层?”Proto UI 需要翻译层,是因为原型表达的不是宿主语法,而是交互语义。
不同宿主之间会天然存在很多差异:
- 能力边界不同
- 性能约束不同
- 渲染模型不同
- 状态管理方式不同
- 引用与副作用处理方式不同
- 最佳实践与生态习惯不同
这些差异不会因为原型足够抽象就自动消失。
原型越想保持宿主无关,翻译层就越需要明确。
没有翻译层,原型只能停留在描述层。
有了翻译层,原型才会变成宿主中的真实产物。
翻译层不只是语法转换
Section titled “翻译层不只是语法转换”翻译层承担的工作并不只是把一种写法改成另一种写法。
很多时候,宿主并不天然具备原型契约所要求的全部能力。
这时翻译层不仅要翻译,还要补全。
例如,在 Web 技术中,不同宿主对 context 的支持程度并不相同。
React 和 Vue 已经有相对成熟的上下文机制,因此对接原型中的 context 契约会比较直接。
但 Web Components 并没有原生的 Context API,这时适配器就需要额外建立一套可工作的上下文机制,把原型中的 context 语义接起来。
样式与反馈也是类似的情况。
在 Web 中,原型里的 Style Token 可以较自然地交给 CSS 体系处理,因此不同 Web 宿主可以共享相近的 Styler。
而在没有外挂样式系统的技术中,例如某些自带渲染策略的 GUI 宿主,翻译层就需要额外的 Styler 模块来承担反馈契约与宿主渲染能力之间的对接。
因此,翻译层通常会同时处理两类事情:
- 把原型语义映射到宿主能力上
- 在需要时补足宿主本身没有直接提供的承接方式
这也是为什么适配器相关的工程约束通常会很多。
原型契约能否进入宿主,最终会集中体现在翻译层上。
Adapter 与 Compiler 分别意味着什么?
Section titled “Adapter 与 Compiler 分别意味着什么?”在 Proto UI 中,翻译层目前主要有两种典型形态:
AdapterCompiler
Adapter
Section titled “Adapter”Adapter 更接近在宿主侧或运行过程中解释原型。
它通常负责:
- 把原型语义接到宿主能力上
- 处理宿主特有的运行时组织方式
- 在需要时补全宿主缺失的承接能力
- 在真实运行环境中维持原型契约
对当前阶段的 Proto UI 来说,Adapter 是更直接的主路径。
它更容易在现有宿主中建立可验证的落地链路。
Compiler
Section titled “Compiler”Compiler 更接近在更静态的阶段,把原型转换为目标宿主的产物。
它处理的通常是更早一步的生成工作,例如:
- 宿主组件代码
- 更稳定的产物结构
- 更接近宿主最佳实践的实现形式
从长期来看,Compiler 会是 Proto UI 的重要方向。
当原型的语义边界足够清晰时,很多翻译工作都可以更早完成,从而获得:
- 更好的性能
- 更强的可分析性
- 更接近宿主习惯的产物形态
两者共享同一套语义基线
Section titled “两者共享同一套语义基线”Adapter 与 Compiler 的区别主要在翻译方式与发生时机。
它们处理的仍然是同一套原型语义:
- 原型定义了什么
- 哪些语义需要被保留
- 哪些差异可以被吸收
这一点不会因为翻译发生在运行时还是更静态的阶段而改变。
翻译为什么可能有损?
Section titled “翻译为什么可能有损?”不同宿主并不共享完全相同的能力边界。
这意味着同一个原型进入不同宿主之后,能够被还原出来的程度本来就可能不同。
有些宿主:
- 更容易承接某类交互
- 更容易保持某类反馈
- 更容易表达某些上下文机制
- 更适合某类渲染与引用策略
另一些宿主则可能:
- 缺少某种原生能力
- 需要更重的工程补全
- 对某种反馈或交互只能近似还原
- 在性能与语义之间必须做现实取舍
因此,翻译出现损耗是正常情况。
Proto UI 关注的重点并不是“所有宿主都必须完全等价”,
而是原型进入不同宿主之后,核心交互语义是否仍然成立。
宿主边界会怎样影响还原?
Section titled “宿主边界会怎样影响还原?”宿主边界会直接影响原型的可还原程度。
这类约束可能来自:
- 宿主的能力极限
- 宿主既有的渲染与状态模型
- 工程上的成本与复杂度
- 不同交互媒介本身的差异
因此,同一原型在不同宿主上,允许存在表现层面的差异。
这里更重要的问题是:
这些差异是否仍然在原型允许的语义边界之内。
如果差异只是宿主表达方式不同,而核心交互责任没有变化,
那么它仍然可以被视为同一个原型的合理落地。
如果差异已经破坏了原型的关键通路、边界或时序语义,
那就不再只是还原程度的问题,而是原型没有被正确承接。
host 通路默认不在跨平台主承诺范围内
Section titled “host 通路默认不在跨平台主承诺范围内”Proto UI 可以承认组件与宿主之间存在直接关系。
这类关系在很多场景下都很重要。
但在默认情况下,host 通路并不属于 Proto UI 的跨平台主承诺范围。
原因很直接:
- 宿主越具体,能力越难抽象
- 越贴近宿主本身,越难成为跨宿主共享的协议内容
这并不妨碍 host 通路存在,
也不妨碍它在某些适配器中承担重要责任。
只是对于跨平台原型来说,更稳定、更容易共享的通路仍然会优先进入核心承诺范围。
因此,越靠近宿主本身的能力,越需要依赖翻译层来处理。
翻译层意味着什么?
Section titled “翻译层意味着什么?”翻译层的存在,说明原型与宿主实现之间本来就有一段需要被处理的距离。
原型描述的是交互语义,
宿主承载的是具体实现。
Proto UI 把这两者分开处理,因此也需要把中间这一步单独拿出来。
这一步就是翻译层。
它负责把原型接到宿主能力上,
也负责在必要时补全宿主缺失的承接方式。
原型越独立,翻译层的重要性就越明显;
宿主差异越真实,翻译层承担的责任也就越重。
这一篇没有展开什么?
Section titled “这一篇没有展开什么?”为了保持主线清晰,这一篇不会继续展开:
Adapter的具体 API 设计- 各宿主的能力矩阵
Host Caps的细化约束- 具体的
Adapter Guide - 不同翻译层在工程上的模块化组织
这些内容会在后续工程章节和契约文档中继续讨论。
如果翻译层已经成立,那么接下来的问题就是:
为了让原型、执行语义与翻译层同时成立,Proto UI 需要主动接受哪些约束与取舍?
这正是下一篇 设计约束 要讨论的内容。