Overreacted

UI工程的要素

2018 M12 30 • ☕️ 1 min read

在我之前的文章中,我讨论了关于我们存在知识漏洞问题。你们也许会总结为我提倡平庸,并不是,这是很宽的领域。

我坚信你能从任何地方开始学习,并且不需要遵从特定的顺序。但是获取足够的经验还是有很大的价值的。我个人已经对创建用户界面非常感兴趣了。

我一直在思考我真正了解和认为有价值的是什么。当然,我熟悉一些技术(比如JavaScriptReact)。但更重要的经验教训是难以捉摸的。我从未试图用语言表达出来。这是我第一次尝试将它们罗列出来并且对它们进行描述。


市面上有很多关于技术或库的学习路线。哪个库将在2019流行?2020又会是什么?你应该学习Vue或者React吗?AngularRedux或者Rx吗?你需要学习ApolloREST或者GraphQL吗?这是很容易迷茫的。如果作者错了呢?

我学习的最大突破不是学习一门固定的技术。而是,当我在解决一个特定的UI问题时学到了很多。有时,我会在之后发现一些库或者同伴帮助了我。在其他情况下,我会想到自己的解决方案(好的或者坏的)。

理解问题、尝试使用解决方案、应用不同的策略,这些组合使得我获取了最具价值的学习经验。这篇文章主要就是探讨这些问题的。


如果你主要工作于用户页面,你可能处理了不少这样的挑战——直接的或使用库。无论如何,我鼓励你创建一个不依赖库的小应用,然后进行复现和解决这些问题。它们都没有一个正确的解。学习来自于探索问题领域和尝试不同可能的权衡。

🤔列举出页面开发中的一些问题,自己思考解决


  • 一致性(Consistency)。 你点击了“like”按钮后,文本变成了:“你和你另外的三个朋友喜欢了这篇文章。”你再此点击,文本又变回去了。听起来很简单吧。但是在屏幕上好几个地方都存在这样的标签。也许有一些其他的提示需要改变(例如按钮的背景色)。‘likers’列表已经提前从服务端获取到了,并且当鼠标移上去的时候也应该能够看到你的名字了。如果你导航到另一个屏幕并返回,博客不应该“忘记”它被喜欢过。即使是局部一致性本身,也会带来一些挑战。但是其它的用户可能也会改变我们展示的一些数据(例如喜欢我们正在看的帖子)。我们怎样保证屏幕上不同部分同步相同的数据呢?我们如何以及何时使本地数据与服务器一致,反之亦然?
  • 响应性(Responsiveness)。 人们只能在有限的时间内忍受他们的行为缺乏反馈。对于手势和滚动等连续性操作,这个限制很低。(即便是跳过一个16ms的帧也会让人感觉很“不爽”。)对于像点击这样的离散操作,有研究表明,用户认为任何小于100ms的延迟都很快。如果一个动作需要更长的时间,我们需要显示一个视觉指示器。但也有一些违反直觉。导致页面布局的“跳跃”或经历几个加载阶段的指示器会让动作感觉比以前更慢。类似地,在20ms内处理交互(以丢帧为代价)比在30ms内处理交互要慢,而且没有删除帧。大脑并不是基准。我们如何保证我们的应用响应不同的输入呢?
  • 延迟(Latency)。 计算和网络链接都需要时间。有时,如果不影响目标设备的响应能力,我们可以忽略计算成本(确保在低配的设备上测试过你的应用)。但是处理网络延迟是无可避免的——它将消耗几秒钟!我们的应用不能静止等待数据或代码加载。但是那有可能发生在每一个屏幕中。如何在不显示“loading”加载或空白情况下优雅地处理延迟呢?如何避免“跳跃”布局呢?以及如何在每次不“重新连接”代码的情况下更改异步加载项呢?
  • 导航(Navigation)。 当我们与页面交互时,我们期望UI能够保持“稳定”。事物不应该就在我们眼前消失。导航,无论是在应用程序内部启动(如单击链接),还是由于外部事件(如单击“后退”按钮),都应该遵从这一原则。例如,在配置文件屏幕上的/profile/likes/profile/following选项卡之间切换不应该清除选项卡视图之外的搜索输入。尽管导航到另一个屏幕中就像走进了一间房间一样。人们期望能够走回去并能找到他们留下的东西。如果你在导航中,单击一个链接,然后返回,你失去了在导航中的位置,这是令人沮丧的——或者等待它再次加载。我们如何在不丢失重要上下文的情况下设计应用程序来处理任意导航。
  • 陈旧(Staleness)。 通过引入本地缓存,我们可以使“后退”按钮导航立即生效。在缓存中,我们可以“记住”一些数据,以便快捷访问,即使理论上我们可以重新获取它。但是缓存自身也存在着一些问题。缓存可能会过期。如何处理当我改变了一个头像,缓存也应该会更新的问题。如果我创建了一篇新的文章,也应该立即出现在缓存中,否则缓存将是无效的。这将变得困难且容易出错。如果发布失败了呢?缓存在内存中停留多长时间?当我们重新获取提要时,是将新获取的提要与缓存的提要“整合”,还是将缓存丢弃?如何在缓存中表示分页或排序?
  • 熵(Entropy)。 热力学第二定律是这样说的,随着时间的推移,物质会变得一团糟(嗯,不完全是)。这也同样适用于用户界面。我们不能准确地预测用户交互及其顺序。在任何时间点,我们的应用程序可能处于数量惊人的状态下。我们尽最大努力使结果可预测并且限制我们的设计。我们不想看到一个bug截图,然后疑惑“这是怎样发生的?”。对于N个可能的状态,它们之间有N*(N - 1)个可能的跃迁。例如,如果一个按钮可以处于5种不同的状态之一(正常,激活,悬停,危险,禁用),对于5×4=20个可能的转换,更新按钮的代码必须是正确的——或者禁止其中一些。我们如何控制可能状态的组合爆炸,并使视觉输出可预测?
  • 优先级(Priority)。 有些事情比其它事情更重要。对话框可能出现在产生它的按钮的“上方”,并“跳出”其内容的剪辑边界。新调度的任务(例如响应单击)可能比已经启动的长时间运行的任务(例如在屏幕折叠下方呈现下一篇文章)更重要。随着我们应用程序的成长,它的部分代码是由不同的人和团队编写的,它们争夺有限的资源,如处理器、网络、屏幕空间和包大小预算。有时您可以根据“重要性”的共享级别对竞争者进行排序,比如CSS z-index属性。但是它很少有好的结局 每个开发人员都偏颇地认为他们的代码很重要。如果一切都很重要,那么什么都不重要!我们如何让独立的小部件合作,而不是争夺资源?
  • 可访问性(Accessibility)。 无法访问的网站不是一个小众问题。例如,在英国,每5个人中就有1人患有残疾。(这是一张不错的信息图表)我也有这种感觉。虽然我只有26岁,但我还是很难阅读字体细、对比度低的网站。我试着减少使用触控板的次数,我担心有一天我将不得不通过键盘来浏览功能不佳的网站。我们需要让我们的应用程序对于有困难的人来说不那么可怕——好消息是有很多唾手可得的成果。首先是教育和工具。但我们也需要让产品开发人员做正确的事情。我们可以做些什么来使易访问性成为默认而不是事后考虑?
  • 国际化(Internationalization)。 我们的应用程序需要在全世界各地运行。人们不仅会说不同的语言,而且我们还需要用产品工程师最少的工作量来支持从右到左的布局。我们如何在不牺牲延迟和响应性的情况下支持不同的语言?
  • 交付(Delivery)。 我们需要将应用程序代码发送到用户的计算机。我们使用什么传输和格式?这听起来很简单,但是这里有很多权衡。例如,本机应用程序倾向于提前加载所有代码,代价就是使得应用程序变得更大。Web应用程序的初始负载往往较小,但在使用过程中会有更多的延迟。我们如何选择在哪个点引入延迟?我们如何基于使用模式优化我们的交付?最优解需要什么样的数据?
  • 伸缩性(Resilience)。 如果你是昆虫学者你可能会喜欢bugs,但你可能不喜欢看到它们出现在你的程序中。然而,你的一些bug将不可避免地进入生产环境。然后会发生什么?一些bug会导致错误但定义良好的行为。例如,你的代码可能在某些条件下显示不正确的输出。但是如果渲染代码崩溃了呢?那么我们就不能有意义地继续,因为视觉输出会不一致。一个帖子的崩溃不应该“拉下”整个feed,也不应该让它进入一个导致更多崩溃的半中断状态。我们如何编写代码来隔离渲染和获取失败,并保持应用程序的其余部分运行?容错对于用户界面意味着什么?
  • 抽象(Abstraction)。 在一个小的应用程序中,我们可以硬编码许多特殊的情况来解决上述问题。但是应用程序往往会增长。我们希望能够重用、分叉和联接代码的各个部分,使它们能够一起工作。我们想在不同的人熟悉的片段之间定义清晰的界限,避免使经常变化的逻辑过于僵化。我们如何创建隐藏特定UI部分实现细节的抽象?我们如何避免在我们的应用增长过程中再次引入我们刚刚解决的问题?

当然,还有很多我没有提到的问题。这个列表绝不是详尽的!例如,我没有谈到设计人员和工程协作,或者调试和测试。也许下一次我会写的更详尽。阅读关于这些问题的文章时,很容易想到使用特定的视图库或数据获取库作为解决方案。但是我鼓励你假装这些库不存在,从这个角度再读一遍。您将如何解决这些问题?去试试构建一个小的应用程序吧!(我很乐意看到你在GitHub上做的实验——你可以发推特(Twitter)回复我。)

这些问题的有趣之处在于,它们中的大多数都能以任何规模出现。你可以在诸如typeahead或工具提示这样的小工具中看到它们,也可以在诸如Twitter和Facebook这样的大型应用程序中看到它们。

想想你喜欢使用的应用程序中的一个非试验性的UI元素,仔细检查一下这个问题列表。您能描述一下它的开发人员所选择的一些折衷方案吗?尝试从头创建一个类似的行为!

通过在不使用库的小型应用程序中试验这些问题,我学到了很多关于UI工程的知识。我向任何想对UI工程中权衡利弊的人推荐同样的方法。