Xilem:Rust中的UI架构


由于各种原因,Rust是一种用于构建用户界面的吸引人的语言,特别是它承诺提供性能和安全。然而,找到一个好的架构是具有挑战性的。在其他语言中运行良好的架构通常不能很好地适应Rust,主要是因为它们依赖于共享的可改变的状态,而这并不是Rust的习惯。
由于这个原因,有时会有人断言Rust不适合于UI。
我一直认为有可能找到一个非常适合在Rust中实现的UI架构,但我之前的尝试(包括目前的Druid架构)都是有缺陷的。我研究了一系列其他的Rust UI项目,觉得这些项目也都没有合适的架构。

这篇文章提出了一个新的架构,它是对现有工作和一些新想法的综合。我们的目标包括用易于组合的组件来表达现代反应式、声明式的UI,以及高性能的实现。对于那些熟悉SwiftUI、Flutter和React等先进工具包的人来说,用这种架构编写的UI代码将显得非常直观,同时也是Rust的术语。

Xilem "这个名字来自于木质部,这是维管束植物(包括树木)的一种运输组织。这个词在包括罗马尼亚语和马来语在内的几种语言中都是用 "i "来拼写的,它指的是xi-editor,这是探索Rust中UI的一个起点(现在被搁置了)。

像大多数现代UI架构一样,Xilem是基于视图树的,它是对UI的简单声明性描述。对于增量更新,视图树的连续版本会被差异化,而结果会被应用到小部件树上,这更像是一个传统的保留模式的UI。Xilem的核心还包含一个增量计算引擎,具有精确的变化传播,专门用于用户界面。

Xilem最创新的方面是基于ID路径的事件调度,在每个阶段提供对应用状态的可变访问。一个独特的功能是适应节点(Druid中透镜概念的演变),它有助于组件的组成。通过将事件通过适应节点进行路由,子组件可以访问与父组件不同的可变状态参考。

对现有架构的快速浏览
这个架构的设计是为了解决现有技术水平的局限性和问题,包括目前的Druid架构和其他尝试。如果不了解这些架构,就很难理解其中的一些动机。也就是说,对反应式用户界面架构的全面调查将是一项相当长的工作;本节只能触及其中的重点。

现有的Druid架构有一些很好的功能,但我们一直看到人们在共同的主题上挣扎。

  • 创建静态widget层次结构和动态更新它们之间有很大的区别。
  • 应用程序的数据必须有一个数据绑定,这意味着克隆和平等测试。内部可变性实际上是被禁止的。
  • 镜头lens机制是混乱的,实现复杂的绑定模式并不容易。
  • 我们从未想出如何以一种令人信服的方式整合异步。
  • 有一种环境机制,但它并不高效,而且不支持细粒度的变化传播。

另一个常见的架构是即时模式GUI,包括相对纯粹的形式和修改后的形式。它在Rust中很受欢迎,因为它不需要共享可变的状态。它还得益于整体系统的简单性。
然而,该模式在很多方面都过于简化了,而且很难做到复杂的布局和其他模式,而这些在保留的部件系统中则更容易。
还有许多与有时渲染陈旧状态有关的纸上谈兵。(我在 "crochet "架构实验中用保留部件的后端模拟即时模式的API进行了实验,结论是结果并不令人信服)。
流行的egui crate是即时模式的可靠实现,makepad也是基于它的,尽管它在一些重要方面有所不同。

Rust中一个特别常见的UI架构是The Elm Architecture,它也不需要共享可变的状态。相反,为了支持来自UI的交互,手势和其他相关的UI动作创建了消息,然后被发送到一个更新方法,该方法采取中央应用程序状态。
Iced、relm和Vizia都使用这种架构的某种形式。一般来说,它工作得很好,但需要创建一个明确的消息类型并对其进行调度,这很繁琐,而且Elm架构并不像其他一些架构那样支持干净的派生组件。Elm文档特别警告不要使用组件,说:"在Elm中积极尝试制作组件是灾难的根源"。

最后,有一些认真的尝试将React模式移植到Rust中,其中我认为Dioxus最有希望。这些依赖于内部可变性和其他模式,我认为这些模式对Rust的适应性很差,但绝对代表了这里提出的想法的一个可靠的替代方案。我想我们将不得不建立一些东西,看看它们的效果如何。

同步的树
Xilem的架构是围绕着生成树和保持它们的同步而建立的。以这种方式,它是对我以前的博文中所描述的想法的完善,即建立一个统一的反应式用户界面理论。

在每个 "周期 "中,应用程序产生一个视图树,并从该树中获得渲染。这个树的寿命相当短;每次更新UI,都会产生一个新的树。从这里,一个部件树被建立(或重建)。视图树只保留足够长的时间来协助事件调度,然后与下一个版本进行比较,这时它就被放弃了。相比之下,部件树在不同的周期内持续存在。除了这两棵树之外,还有一棵包含视图状态的树,它也是跨周期保存的。(视图状态的功能与React钩子非常相似)。

在现有的UI架构中,视图树与SwiftUI的架构最为相似:
视图树中的节点是普通的值对象。它们也包含回调,例如指定点击按钮时要采取的行动。
和SwiftUI一样,但对于动态语言中的UI来说,视图树是静态类型的,但在严格的静态类型过于严格的情况下,有一个类型化的逃生舱(Swift的AnyView)。

这些树的Rust表达是View trait的实例,它有两个相关的类型,一个是视图状态,一个是相关的部件。状态和部件也是静态类型的。这个设计在很大程度上依赖于Rust编译器的类型推理机制。除了推断视图树的类型外,它还使用关联类型来推断关联状态树和部件树的类型,这些类型在编译时是已知的。在几乎所有其他类似的系统中(SwiftUI是一个明显的例外),这些都是在运行时通过相当数量的分配、下转换和动态调度来确定的。

。。。
详细点击标题

总结:
这里的一个难点是,在为 Web 基础设施构建的反应式架构上做了大量工作,并且很难将这些与为原生 UI 构建的东西进行比较。解决这个问题的一种方法是根据 Xilem 的想法构建一个原型响应式 Web 引擎。