翻译:基于函数的组件 API(扩展讨论)#55

313 阅读12分钟
  • 译按:Vue Composition API 提案曾在社区引起较大的争论,前几天读到这个 issue,看到 Vue 用户意见和尤雨溪回复,对 Composition API 和 React Hooks 的设计理念和解决问题都有了更深的理解,特此翻译。
  • 原文:github.com/vuejs/rfcs/…

@abalashov:

@yxx990803 的要求,在这里开一个 issue 贴上我在 #42 下的原评论:

我是“给 Vue 的情书”的作者:www.evaristesys.com/blog/love-l…

作为一个欣赏它与众不同的特性以及与竞争框架平起平坐的表现的 Vue 的大力推动者,我必须要说我对函数 API 提案感到不安和失望。我很赞同上面的帖子,没有什么严重的问题值得用这么彻底的改变去解决,并且本质上,这个提案相当于追逐闪光的新事物。诚然,的确存在一些代码拆解和模块化问题要改变路线。但并非所有理论上的问题都值得去改变。

Vue 最大的优点之一,正如我在文章中提到的,在于,在 2019 年中,它仍然在写法上和 2016 年末我第一次接触 Vue 几乎一样。这和 JS/web 世界里常见的修改和弃用速度形成鲜明对比。Frank Chimero 在这个问题上高水平、敏锐的看法值得在此提及: frankchimero.com/writing/eve…

从原则和哲学的视角看,我认为理解引入任何形式的彻底的 API 改变对任何项目都是不兼容这点至关重要,且提供对旧 API 的向后兼容不解决这个问题。在认可 2.x API 没有弃用计划并费力强调的这一事实下,你仍然认为它在整个生态系统中是落日余晖。2.x API 不再是一等公民。一个意味着倾向于新的做事方法、示例代码、教程和书籍等的全新方式,将必然随时间适应它,造成对那些在海量 Vue 代码中投入者的管理和方向灾难。新 API 不可避免地成为思想陈述,并且会削弱旧 API -- 的确,“旧” vs “新” 的存在 -- 有一种思想效价。

当变化不可避免地时候,人们希望遵从“最佳实践”试图避免钻头腐朽,并对接受当今思潮有心理压力。所以,提供旧的选项 API 支持没那么大帮助;这个 RFC 中保证维护 2.x 选项 API 的向后兼容性并不坚定,并会随时间慢慢脱轨,就像和平号空间站。实际上,你在裁定这有一个新的做事方式,并且它们应该被这样做。

我也同意上面的缺点章节的批评:对这种方式的晦涩和容易导致复杂的“面条式代码”感到担忧。Vue 最大的卖点之一是简单易用。它足够静态,一个主要的 Vue 的组织承诺有保质期和耐久性,而不是在手中蒸发,而太多其他与 JavaScript 相关的承诺,却反复无常地改变 API 和架构。

因此,关于代码组织和模块化的技术争论,从商业和生态健康视角,我认为在 RFC 里应该被看轻,而改进带来的不利之处(微小的个人观点)应该小心和明智地看重。我没有看到一个足够吸引人的问题值得如此激进的解决方案,尽管在一段时间 2.x 路线承诺被谨慎关注。丢弃让 Vue 变好的东西不值得。

我是土生土长的俄罗斯人,关于这点有一个古老的政治轶事:

拉扎尔·卡冈诺维奇给斯大林带来一个缩小的再建的莫斯科模型,一个斯大林设想的宏伟的全球社会主义首都(就像希特勒和施佩尔的千年德国建筑的翻版)。大部分革命前莫斯科的历史符号特色被毁灭,取而代之的是宏大的广场和巨大的建筑,彰显具有阶级意识的无产阶级革命,大元帅的宏大未来愿景和摆脱资本主义大厦的热情、美学及告别过去的理想主义。

斯大林问:“拉扎尔,圣巴西勒教堂在哪?”

“它被移除了,它将被拆毁。”

斯大林叹了口气翻了个白眼。

“拉扎尔,把它放回去。”

@yyx990803:

我尊重你的观点所以请不要将下面的看成私人意见。

没有什么严重的问题值得用这么彻底的改变去解决,并且本质上,这个提案相当于追逐闪光的新事物

我非常不同意这个。逻辑组合可能是大型项目最严重的问题之一。引用我在另一个帖子所说:

  • RFC 帖子中也有 很多 表达他们如何发现这个 RFC 正好立即解决他们所面对的问题的用户。并且他们提到当前基于对象的语法恰恰是阻碍他们项目规模的瓶颈。我承认有很多像你那样可能从未体验过这种经历的人(完全可行,这很好!),但是如果一个美感选择应真实存在的维护负担而来(诚然仅在某些类型的项目中),那么它将不再仅仅是美感问题。由于另一些人的美感偏好而否定一些人用更好的抽象解决他们的问题,似乎是个错误的交易。

Vue 开始很小,但是今天它已经被用于非常广泛范围的项目,伴随着不同水平的复杂度和商业领域。用户处理不同类型的项目遇到不同的需求,有些人能轻松的用基于对象的 API 处理,但也有些人不能。主要的例子是

  1. 封装了多个逻辑任务的大型组件(数百行长)
  2. 多个组件间复用这种逻辑的需求

对于(1),每个逻辑任务被迫在选项类型间分割。例如,一个单纯的获取数据任务可能需要 prop,data 属性,计算属性,mounted 钩子和 watcher 一起工作。这意味着当你拿到这个组件并尝试理解它的数据获取逻辑,你要不断地在选项列表里跳上跳下努力定位与之相关的代码片段。同时,当你扫过一个属性,尽管你知道它的 类型,但说清用于处理哪个 逻辑任务 是有一点难度的。越多的逻辑主题被添加到组件里则情况越糟糕。与之相比,在新 API 下,全部的数据获取相关逻辑能被组织在一起,更重要的是,干净地抽取到一个独立函数,甚至一个独立文件中

和这个问题类似的是项目中的文件组织。我们中的很多人同意以文件类型来组织文件(例如将每件事分解成 htmljscss 文件夹)不能支持规模化。一个功能的相关代码将不得不被分散在 3 个文件夹,仅仅是因为“关注点分离”的错误印象。这里的关键是“关注点”不是以文件类型定义。相反,我们中的大多数选择以功能和职责来组织文件。这正是人们喜欢 Vue 单文件组件的原因。SFC 是一种以功能组织代码的方式。讽刺的是,当 SFC 第一次被引入,很多人抵制它因为他们觉得它违背了关注点分离原则,直到后来认识到 SFC 事实上是更合理的方式去分离关注点。

点(2)已经在 RFC 动机章节有过大篇幅解释,展示它实现混入/高阶组件/局部插槽所能做的但避免了它们的缺点。

鉴于 React Hooks,我们发现了它的一些特点能帮助这些用户解决上述问题。这是我们提出这个提案的根本原因。它的确是“新事物”,但是我们接受新事物因为它代表一种对象存在问题的解决方案,而不仅是因为它们是“新的”。长远考虑,新 API 的实用性将在开发者处理上述问题的时间节省上带来巨大红利。

类型安全也是重要考虑 - 同样的,这是很多用户 迫切 想要的但对那些不使用 TS 的人也许不会显示多大价值的东西。这可以理解 - 但是我认为声称它没有解决任何问题因为被解决的问题没有影响到他的行为有点自私。

引入任何形式的彻底的 API 改变对任何项目都是不兼容,且提供对旧 API 的向后兼容不解决这个问题

“不兼容”定义为用户被迫去修改他们的代码。鉴于用户将不必修改他们已存在的代码,它没有不兼容。在这个方面我不觉得有其他事情需要争论。

即使向后兼容不够,然后你实质上说 一个项目应该不再引入任何激进的新点子,永远的。我认为这是项目政策层面争论,如果我投票,我将坚定地投反对。我们将牢记尽最大努力保证用户的最大利益,但是项目必将发展。

对这种方式的晦涩和容易导致复杂的“面条式代码”感到担忧。Vue 最大的卖点之一是简单易用。它足够静态,一个主要的 Vue 的组织承诺有保质期和耐久性,而不是在手中蒸发,而太多其他与 JavaScript 相关的承诺,却反复无常地改变 API 和架构。

相反,这个提案的动机恰恰是提高长期 Vue 项目的可维护性。

如果我们看任何 JavaScript 项目,全部代码开始于一个入口文件,它本质上是一个在你的应用启动时被调用的隐式 "main" 函数。如果单一函数入口会导致面条代码,那么所有 JavaScript 项目都应该是面条代码 - 显然不是这样。为什么?因为作为开发者我们学过以拆分成模块或函数的方式组织代码。

基于函数的 API 设计的一个核心特点是理解 setup() 里的代码和理解惯用 JavaScript 代码没有任何不同、任何你在组织常规 JavaScript 代码的技术都能用来组织 setup() 函数。任何你的团队写下的应用于普通代码的知识/风格指南/代码 review 流程都可以应用于 Vue setup() 函数。

我同意用新 API 理论上有低的代码质量下限,但是正如提到的可以用你已经在代码库非 Vue 部分防止面条代码的一切做法来减轻。另一方面,用新 API 写的代码也会有大体更高的代码质量上限。任何以新 API 写的代码能被重构成质量高很多的代码、与基于选项的等价实现相比,鉴于基于选项 API 你将不得不借助混入和处理它的缺陷。

我也想指出这个 RFC 不是以简单换取可维护性。我们应该意识到我们在比较你也许用了多年的 API vs 你第一次见的 API 这两者的印象。根本上说,这是你如何思考组件上的转变:

  • 基于选项的 API 认为组件被它包含的属性/方法/选项的类型所定义。
  • 基于函数的 API 认为组件被它封装的逻辑主题所定义。

很多用户在谈论“失去简单性”感到悲哀,实际上是失去以选项类型检查组件的能力。但是在新 API 下,它应该是十分直接地去实现一个组件分析器,该分析器提供一个允许你用属性类型审视组件的视图。换言之我们能够从两个视角来看组件,鉴于选项 API 下受限只有一个(因为逻辑主题在分散于选项的时候丢失了)。

丢弃让 Vue 变好的东西不值得

我已经对重复“没有东西被丢弃”感到厌倦了。但是我们尝试定义什么是真正的“让 Vue 变好的东西”。很多反对这个 RFC 的用户似乎定义为对象语法,就像如果拿走对象语法,就是拿走一切让 Vue 更 Vue 的东西。但是我们看一下什么被原封不动的留着:

  • 模板语法没有变(并且性能更好!)
  • 响应式系统的工作方式没有变
  • 计算属性、watcher & 组件生命周期概念没有变
  • SFC 格式没有变
  • CLI 没有变
  • 框架的渐进式本性没有变
  • 团队承诺提供更好的开发工具没有变
  • 技术上,甚至对象格式都没有变:所有以前能工作的将仍然会工作

Vue 的对象语法从第一天就存在。上述很多是沿途后来添加的,每一个都对 Vue 都贡献了 Vue 的成长。如果你坚信对象语法是对你很重要的全部,我想真诚的请你退后一步,重新思考是什么真正成就了 Vue。毕竟,这个 RFC 不是那么激进的改变尽管它可能看起来像。