本文由 简悦SimpRead 转码,原文地址 yairchu.github.io
Yair的网站
几十年来,开发图形用户界面是很麻烦的,直到2013年Facebook发布了React,它深刻地改变了我们编写图形用户界面的方式,这种变化也传播到了其他平台和库,比如苹果的SwiftUI。
在这篇文章中,我想描述一下现代库与传统库(如Qt、GTK、AppKit等)的区别,并将新方法的早期起源追溯到2003年的 "即时模式UI "范式(ZMW, Dear Imgui)。
传统GUI应用程序的要素
传统上,GUI应用程序的用户代码由以下部分组成。
- 模型代码(模型是应用程序正在查看和编辑的文档)
- 模型的数据结构定义
- 从文件(或从云端)保存/加载数据的代码
- 修改文档并通知其监听器的设定器方法
- 当模型的某些部分发生变化时,监听器机制可以得到通知
- 视图/UI代码
- 为初始文档构建UI元素的代码,它也将注册事件处理程序来处理用户的交互,并将注册监听器来在模型改变时更新UI
- 对用户行为作出反应的事件处理程序。这些将调用模型的设置器来更新文档。
- 当文档发生变化时,监听器处理程序会更新用户界面。
- 解除模型监听器注册的UI对象的析构器
这个结构是很难搞的。我们需要使用我们可能忘记注册的监听器来保持模型和UI的同步,而更新处理程序需要以一种与最初构建相同状态的方式来更新UI,这往往会留下代码重复的痕迹。
React的方法
React重用了UI初始化代码来进行UI更新。它通过比较新的UI描述(又称 "虚拟DOM")和以前的描述,然后根据计算出的差异增加、删除或更新元素。
通过这种方法,我们的模型可以是一个简单的数据结构,而且它不再需要监听器机制或设置器方法。事件处理程序可以直接更新数据。我们也不需要手动编写UI更新代码。
虽然这是一个更简单的方法,减少了模板和重复,但它的缺点是,即使只有文档的一小部分发生变化,也要计算完整的UI描述,这可能会带来性能上的损失。请注意,SwiftUI和Svelte使用跟踪用户代码中的数据依赖关系的语言特性来降低这一成本,只更新数据来源发生变化的用户界面层次。
传统方法的基本原理是
如果React的方法如此简单,为什么像苹果、微软等大公司做的UI库都比较难用呢?他们只是没有找到正确的想法,还是有充分的理由?
答案是,几十年前,计算机的速度比现在慢了好几个数量级,我们需要以最有效的方式为GUI编程,而不是以程序员更容易使用的方式。
比React早得多,2003年Thierry Excoffier发表了"Zero Memory Widgets"研究和GUI库,2005年Casey Muratori发表了一个关于同等方法的视频讲座,他称之为"即时模式GUI" 。
他们的方法可以被描述为 "低级别的 "React。在React中,用户界面的构建结果是由文本框和单选按钮等高级组件组成的DOM,这些组件仍然在浏览器中使用传统的用户界面方法来实现,而Imgui库从头开始构建这种方法。
正如 "零内存 "一词所暗示的,它们不维护任何部件的内存结构。文档是唯一的真理来源!它不需要任何内存结构。库没有遍历它自己的部件结构,而是使用程序员提供的函数遍历文件本身,将文件映射到那一刻的GUI。GUI由两部分组成:它的外观,以及在响应用户事件时调用什么代码。
小部件如何能够消耗 "零内存",或者换句话说,没有状态?因为在一个用户界面中,每次不超过一个 "活动部件"(用户光标当前所在的部件),我们每次只需要维护一个部件的光标和编辑状态,所以只有活动部件实际上有状态。同样,人们可能希望他们的应用程序在打开文档时,滚动条的位置和窗口的大小与用户保存它时相同,甚至这些值也应该是文档本身的一部分,而不是只属于部件的状态。
有了这种方法,就不会有结构的重复,使得它比传统的方法更简单,更不容易出错。然而,由于在每一帧重新绘制整个窗口,它可能会消耗更多的CPU。
免责声明
我对上面提到的所有UI库都没有经验。我没有足够的时间去尝试它们。我在JUCE工作了大约10年,FLTK和Qt各工作了2年,还有一点GTK、AppKit、Kivy,以及一点Web开发的经验。此外,我一直在使用和开发Momentu以及Eyal Lotem开发的Lamdu。
由于我知识的片面性,你很可能在这篇文章中发现不准确的地方或遗漏的关键细节。请随时给我反馈和更正,我会尽力更新。尽管我的知识有差距,但我觉得不得不写这个帖子,因为我在其他地方找不到任何类似的概述。
Momentu
Momentu是一个用于Haskell的声明式/现代GUI库,强调基于键盘的编辑、动画和响应式布局功能。它将在未来的文章中适当讨论。请注意,Eyal在2011年独立地重新发现了其设计基础的现代方法,当时React还没有发布,也是在我们听说Imgui之前。
笔记
- 标题图片来源:可能是monkeyman767?