第七章:在边界中演进
说明当前近似如何以明确边界接受理论、原型库和翻译实践的持续修正。
如果 Prototype 只是我们对组件“本质”的当前近似,Proto UI 应该怎样发展它,又该如何看待没有被官方选择的其他可能?
一条可行路径,而不是唯一答案
Section titled “一条可行路径,而不是唯一答案”白皮书的前两部分共六章,提出了 Prototype 模型,也讨论了它的翻译层。它们共同组成了 Proto UI 对跨技术复用交互语义的一份回答。
但这不是唯一可能的回答。
Proto UI 不打算垄断 Prototype 的可能性,也不打算规定怎样的发展路线才更加“正统”。我们希望看到的,是越来越多能够沉淀人机交互公共资产的尝试,而不是接受同样理念的人,因为选择了不同的实现方式而彼此否定。
这种开放并不影响 Proto UI 自己的边界。Proto UI 当前选择在 Component 层工作:描述一个交互主体跨越技术变化时仍需要保留的身份与义务,再把它翻译到既有 Host。具体业务怎样组合组件、应用怎样调度页面、Host 怎样实现自己的输入与渲染机制,并不会因为它们重要,就自动成为 Prototype 的一部分。
一项能力没有进入 Proto UI 的可移植核心,也不代表它没有价值。这里的区别只是由谁负责:来自具体产品的要求,可以由 Maker 或上层系统承担;依赖某个 Host 的能力,可以留给翻译层或 Host;另一套项目也可以从相同问题出发,作出完全不同的选择。
Proto UI 提出的是一种有边界、可以运行、也可以检验的近似,而不是对问题本身的定义权和解决权。
Proto UI 选择的三条主线
Section titled “Proto UI 选择的三条主线”Proto UI 的工作大致沿着三条彼此关联的主线展开。
原型库逐个探索具体 Component 的身份。
Proto UI 维护 Base 原型库,用它记录尽可能不依赖某种设计语言的基础交互语义;在此之上,也可以形成带有具体视觉与设计取向的原型库。当前由 Proto UI 维护的非官方 shadcn/ui 衍生原型就是这样的例子:它们仍在逐步编目,并在已经明确的范围内继承 Base 提供的交互基础,再增加相应的设计语言投射。
这类工作并不只是增加组件数量。Switch 的行为参数应该怎样设计,Select 在不同媒介下是否允许改变形态,Scroll Area 的哪些 mechanics 应该继续属于 Host——每写出一份 Prototype,我们都在重新检验哪些语义能够跨技术保留。
原型的采用也可以是渐进的。Maker 可以只在确实需要跨技术一致性的局部使用某些 Prototype,而不必先把整个应用改造成另一套框架。
翻译层与生态
Section titled “翻译层与生态”没有翻译层,Prototype 就无法进入新的 Host;而一个新的翻译器也不只服务于一个组件,它可能同时扩大一批既有 Prototype 的落地范围。
这种关系具有一种组合放大效应。比如,当某个设计语言的 Prototype 与某个 Host 的 Adapter 都已经存在,并且 Adapter 能够满足这些 Prototype 所需的 Module、Host Capability 与语义义务时,就不必再为每个组件分别从头编写一套宿主实现。
不过,这并不是无条件的笛卡尔积。一个 Adapter 存在,不代表它已经支持所有 Prototype;一个 Host artifact 能够生成,也不代表所有语义都已得到忠实翻译。能够组合到什么范围,仍然取决于具体能力、翻译结果和证据。
翻译器本身也有持续探索的空间。不同 Adapter 可以用不同方式对接同一个 Host,Compiler 也可能承担更多静态工作。更好的性能、更自然的宿主 API 或更高的保真度,都需要具体实现和证据,而不会仅仅因为采用了某种翻译形式就自动获得。
理论与内核负责维护这些探索共同依赖的表达和运行基础。
一项新的关系是否足以成为 information channel?一个新的 Module 应该拥有哪些责任?当 Prototype 越来越复杂时,怎样扩展表达能力而不让 Host 细节进入可移植语义?这些问题不会随着第一版协议写完就消失。
理论与内核也不只是先于实践作出规定。原型库和翻译层遇到的问题,会反过来检验现有概念是否完整、边界是否合理,以及某项约束到底保护了什么责任。
因此,这三条主线不是互相等待的阶段:理论与内核提供表达基础,原型库探索具体的 Component identity,翻译层让这些近似面对真实 Host;三者又把各自发现的问题送回同一个演进过程。
没有进入主线,依然可能颇具价值
Section titled “没有进入主线,依然可能颇具价值”Proto UI 没有选择的方向,同样可能产生有价值的结果。
例如,有人可能认为 Prototype 理论包含了构建跨端框架所需的某些基础,希望让它进一步负责应用结构、组件组合和框架级调度。这条路线比 Proto UI 当前的 Component 边界更宽,但并不因此是错误的;它只是选择承担另一组责任,也需要面对另一组工程代价。
也可以为特定业务维护定制 Prototype 和定制 Adapter,或者从 Proto UI fork 出更激进、更垂直的版本。甚至完全可以出现另一个同样关注交互知识复用、却使用不同模型和语法的项目。
这些方向不必先获得 Proto UI 的认可才具有价值。反过来,它们的成功、失败和不同取舍,也可能帮助我们发现官方路线忽略了什么。Proto UI 选择维护一条公共主线,并不意味着它是这片问题空间里唯一合法的版本。
实践怎样修正当前近似
Section titled “实践怎样修正当前近似”开放其他可能性,与修正 Proto UI 自己的近似,其实是同一件事的两个侧面:如果 Prototype 不是终极定义,我们就必须允许新的实践指出它哪里不够好。
仍以 Switch 为例。前几章中,我们从 Switch、Toggle 与 Checkbox 的差异开始,把 Switch 拆成 Root 和 Thumb,描述 State、Lifecycle 与 information channel,再尝试把这些语义翻译到不同 Host。
这些工作会增加我们对当前 Switch Prototype 的信心,却不能证明它已经完整。新的实现可能发现某项操作无法表达,新的交互媒介可能让已有 Feedback 失去意义,某个非 Web Host 也可能暴露出我们误把 Web 的习惯当成了通用语义。
面对这些现象,我们不能把所有问题都塞进 Prototype,也不能把它们全部推给 Adapter。不同的失败指向不同的修正对象:
- 如果 Prototype 已经说清了义务,只是某份实现没有做到,这是实现偏差;
- 如果语义能够成立,但翻译层尚未获得必要的 Host 能力,这是翻译能力的缺口;
- 如果必要的 Switch 语义从未被描述,或者现有定义对某类 Host 过拟合,需要修正的是 Switch Prototype;
- 如果失败表明新的参与者关系无法被现有 information channel 解释,甚至出现了“没有任何外部关系却仍必须是 Component”的反例,需要重新检查的就是更基础的理论。
Proto UI 所期望的演进循环是:提出近似,将其写成 Prototype,通过 Core、Runtime 与翻译层使其落地,再从符合性测试、实现失败和真实使用中收集证据,判断哪一层的理解出现了问题,并明确修正它。
这里的“明确”很重要。
一次实现与预期不符,不会自动把当前实现变成新标准;一份 Prototype 已经能够运行,也不意味着其中的每个判断都是稳定真理;实践中的反例值得认真对待,但仍需判断它揭示的是局部缺陷,还是足以修正更普遍的理论。
白皮书负责给出哲学方向,Spec 把其中可以工程治理的部分细化成可检查的义务,实现和测试再提供当前行为的证据。实践可以推动白皮书、Spec、Prototype 或翻译边界发生变化,却不应该静默覆盖它们。否则,我们无法分辨自己是在修正认识,还是只是在替偶然的实现结果寻找解释。
仍然需要更远的检验
Section titled “仍然需要更远的检验”目前 Proto UI 的可执行证据仍然主要来自 Web family。React、Vue 与 Web Component 之间的实践很有价值,但它们共享了大量基础条件,不能单独证明这套近似在 Flutter、Qt 或未来尚未出现的技术中同样成立。
所以,新的非 Web 实现并不只是扩大支持列表。不同的结构、输入、生命周期和渲染模型,会检验现有抽象究竟能够走到哪里。如果未来证据表明某些语义无法跨越这些边界,合理的结果可能是改进翻译层、修正 Prototype、缩小理论的适用范围,或者承认某种组合并不受支持。
另一套框架、一个社区 Prototype,甚至另一个 Proto UI,也可能带来同样重要的证据。它们不必沿着官方路线前进,才能帮助人们更准确地认识这片问题空间。
一个允许被反例缩小、被实践纠正,也允许其他答案存在的近似,比一套能够解释所有结果、因而永远不会失败的理论更有价值。
如果这样的探索能够长期持续,我们保存的就不只是一代组件代码,而是一份可以继续被新技术检验、承接和修正的交互知识。