React 团队的原则是什么?
December 25, 2019
在我加入 React 团队的这段时间里,我有幸见识到 Jordan、Sebastian、Sophie 以及其他资深团队成员是如何解决问题的。本文中,我将从他们身上学到的内容提炼为几个高层次的技术原则。这些原则并不全面,只是我个人对 React 团队运作方式的总结——其他成员可能会有不同的看法。
UI 优先于 API
每一种抽象在大规模应用时都会暴露出自身的特殊性。这些特殊性会如何在用户界面中体现?你能否通过界面判断出某个应用是基于哪种抽象构建的?
抽象会直接影响用户体验——它可能让某些体验成为可能、持续存在,甚至让某些体验变得无法实现。因此,在设计 API 时,我们不会从抽象本身出发,而是从期望的用户体验出发,反向推导出需要的抽象。
有时候,在这个反向推导的过程中,我们会发现需要彻底改变原有的思路,才能实现理想的用户体验。如果一开始就从 API 出发,是无法发现这些问题的。所以我们始终坚持 UI 优先于 API。
吸收复杂性
让 React 内部实现变得简单并不是我们的目标。如果让 React 内部更复杂,能够让产品开发者的代码更易于理解和维护,我们愿意承担这份复杂性。
我们希望让产品开发能够去中心化、协作化。通常,这意味着我们要承担中心化的负担。React 不能被拆分成小而简单、松耦合的模块,因为要完成它的使命,必须有一个协调者的角色——这正是 React 的定位。
通过提升抽象层级,我们让产品开发者变得更强大。他们能够从整个系统所具备的某些可预测特性中受益。但这也意味着,每当我们引入第 N+1 个新特性时,它都必须与已有的 N 个特性良好协作。这也是为什么为 React 贡献新特性在设计和实现层面都非常困难,也导致“核心”开源贡献并不多见。
我们吸收复杂性,是为了防止这些复杂性渗透到产品代码中。
先有 Hack,再成规范
每个 API 都会带来新的限制。有时候,这些限制会阻碍人们实现令人愉悦的用户体验。为此,我们提供“逃生通道”,让开发者在必要时能够绕开我们的限制。
Hack(临时解决方案)无法长期存在,因为它们很脆弱。产品开发者必须在维护 hack 带来的负担和牺牲用户体验以移除 hack 之间做出选择。通常,用户体验会因此受损,或者 hack 本身阻碍了进一步的改进。
我们需要允许通过“逃生通道”实现 hack,并观察大家实际采用了哪些 hack。我们的职责是,最终为那些为了更好用户体验而存在的 hack 提供规范化的解决方案。有时候,这个过程可能需要数年。相比于固化糟糕的规范,我们更倾向于灵活的 hack。
支持局部推理
在代码编辑器里,你能做的无非是添加几行、删除几行,或者复制粘贴一些内容。然而,许多抽象却让这些基本操作变得困难。
比如,MVC 框架通常会让你无法安全地删除某部分渲染输出。这是因为父组件会以命令式的方式调用子组件的方法(而你删除后这些子组件已经不存在了)。相比之下,在 React 中,通常只需删除渲染树中的几行代码即可,这是一种优势。
在设计 API 时,我们假设开发者只了解自己正在处理的这部分代码。对于局部的预期效果,我们希望避免出现意外结果。例如,通常我们期望添加代码是安全的,删除或修改代码时,也能清楚地知道需要关注哪些相关代码。我们不能假设开发者在修改单个文件时了解整个代码库。
当某些操作不安全时,我们希望开发者能尽早发现变更的全部影响。警告、类型检查和开发者工具都能提供帮助,但它们的能力受限于 API 的设计。如果 API 不够约束,局部推理就无法实现。例如,这也是为什么 findDOMNode()
不好用——它需要全局知识。
渐进式复杂度
有些框架会让你在某个节点上面临选择:提供两种做法,一种简单易学,一种功能强大。简单方式易于上手,但用到一定程度后会遇到瓶颈,届时你不得不推翻之前的实现,重新用另一种方式实现。
我们更希望,实现复杂功能和实现简单功能在结构上差异不大。例如,我们不会单独提供一套“适用于简单场景”的模板 DSL,因为这会造成分叉。我们愿意在入门门槛上做出妥协,因为你很快就会需要完整的功能机制。
有时候,“简单方式”和“强大方式”甚至是两个不同的框架,这样你还得重写代码。我们也希望尽量避免这种情况。例如,添加服务端渲染在 React 中虽然需要做一些额外工作,但并不需要彻底重写。
控制损害范围
自上而下的工具,比如代码体积预算,是很重要的。然而,从长远来看,我们的标准总会有所松懈,功能会在截止日期前上线,产品也可能无人维护。当我们无法指望所有人都遵守规范时,作为协调者,我们的职责就是控制损害范围。
如果某些 UI 代码运行缓慢或超出预算,我们需要尽可能减少它对加载时间和与其他 UI 部分交互的负面影响。理想情况下,开发者只需为他们用到的功能“买单”,用户也只需为他们实际交互的 UI “买单”。并发模式下的 Time Slicing 和 Selective Hydration 等特性,正是为了解决这些问题而设计的不同手段。
由于库代码的开销相对稳定,而应用代码的规模是无限的,我们更关注如何控制应用代码带来的损害,而不是库代码的固定成本。
相信理论
有时候,我们已经知道某种方案是死路一条。也许它现在还能用,但我们已经意识到它的局限性,而这些局限性从根本上阻碍了我们实现理想的用户体验。一旦有可能,我们就会果断放弃这种方案。
我们不希望陷入局部最优。如果有另一种理论上更合理的方案,我们愿意投入精力去实现它,即使这需要很多年。在实现过程中会遇到各种障碍和务实的妥协,但我们相信,只要持续努力,最终理论会胜出。
你们团队的原则是什么?
以上是我观察到的我们团队在工作中的一些基本原则,但肯定还有很多没提到。我也没有涉及到一些非技术层面的原则,比如如何发布 API、如何沟通变更等,这些可以留到以后再聊。
你们团队有一套自己的原则吗?欢迎和我分享。
本文最初发布于 这里。
Pay what you like