Posts

React 与 Vue.js

2023年5月25日 / 2023年10月17日

我对 Vue.js 这个前端框架天生有着好感。这是我的第一款前端框架,甚至此前我都没有用过 Javascript。Vue.js 拥有完善的中文文档,让一无所知的我也能快速入门前端。如今,我依旧喜欢它的直观性、易用性以及优雅的设计。

然而 Vue 并不是全球范围内的第一选择,这个世界上还存在着一个名为 React 的怪物,它是事实上的最优先选择,前途一片光明,无人能够撼动它的地位。Vue 和 React 都是为了解决同一个问题(如何更好地编写 UI)而存在的,它们是可以互相替代的工具。

虽然我不是什么高级前端工程师,我对这两个库的底层只有文档上的粗浅了解,而没有深入底层。但我对两个框架/库都有一点点使用经验,也算是一个熟练使用者。我很想向大家分享我在使用这些库时的一些经验和体会。希望能帮助到一些仍在抉择的初学者。

Vue 的优点,和 React 的不足之处

开发体验

Vue 明显地注重提升开发体验(Developer Experience, DX)。它提供了双向绑定、各种工具和自动导入组合式函数的语法糖。在这方面,Vue 团队投入了更多的努力。Vue 内置的 Transition 比 React 生态中的第三方 Transition 库更易用。在某些方面,Vue 的生态系统似乎更为繁荣,例如 Vite 和 Unjs 等工具,它们不仅支持 Vue,还回馈整个前端开源社区。官方还将常用的开发工具集成为模块,使项目保持简洁,让我们能够更专注于业务逻辑。此外,Vue 和 Nuxt 的核心团队还维护着强大的开发工具,比传统的浏览器插件更加优秀。我没有看到包括 React 在内的其他前端框架在这方面走得如此远。

Composition API 与 React Hook

我的结论是,Vue 的 Composition API 比 React Hook 更易用。尽管 Hook 看起来只是普通的函数,但实际上使用起来有很多限制。例如,它不能出现在条件语句内,从用户角度来看,这很奇怪。具有 " 缓存 " 功能的 Hook 需要手动传入依赖数组,否则不会更新。最佳实践是使用 eslint 作为辅助工具,它可以自动填写依赖,但它并不总是智能,有时我们可能需要在不想在某个变量改变时触发更新的情况下通过注释来禁用 eslint。这并不是一种优雅的解决方案。相比之下,Vue 的 watchEffect 会自动收集依赖,如果需要更精细的控制,我们可以手动传入需要监听的变量作为 watch 的第一个参数。

黑魔法

很多人批评 Vue 的 API 太多,充斥着太多 " 魔法 "。然而,实际上,我们经常只使用 ref、watchEffect 以及像 v-for、v-if 和 slot 这样的功能。而且,所有这些 Vue 的 " 魔法 " 都旨在提高编写业务代码的效率,代价只是稍微查看文档。在我看来,这个代价是值得的。

相比之下,React 的设计更为纯粹。React 提供了一个简单的指导:用纯函数表达 UI。只要渲染函数的 props 保持不变,重复多次调用它都会产生相同的结果。

然而,React 也提供了 Hook。Hook 同样是一种 " 魔法 ",它赋予了组件执行副作用、状态管理和从上下文中检索数据的能力。尽管这些功能是必要的,但它们可能超出了渲染函数的本职工作。

使用 React,通过 Hook 可以在渲染函数内定义副作用、状态和上下文。但为什么一定要这么做?Hook 看上去就像在函数中声明一些变量,但实际上是由 React 管理的。从直觉上看,每个 Hook 都会被多次执行,但事实并非如此,Hook 的声明更像是在渲染函数之外独立存在。

我认为渲染函数应该尽可能简单,不应包含副作用,保持纯函数即可。因此,props 和模板足够了。其他 Hook 功能,为什么要将它们放在渲染函数中呢?将它们放在只执行一次的 setup 中,在依赖变化时重新设置就足够了。因此,我们发现我们不再需要 useCallback、useMemo,也不需要 useEffect。这样,我们获得了更为简单和清晰的实现,这就是 Vue。

在 React 世界中,我们不仅需要查看文档,还需要寻找其他开发者总结的各种经验和教训,例如如何使用 useEffect 1。心智负担没有减少,只是转移了。我们确实不再需要记住 API,但需要避开一个个陷阱,寻找最佳实践。

TypeScript 支持

有人认为 Vue 对 TypeScript 的支持不够好,因此远离它。然而,在非开发库的业务开发情景中,我认为 Vue 并不特别需要 TypeScript。不使用 TypeScript 的 Vue 在易用性和严谨性方面已经有了平衡。并且现在的 Vue 对 TypeScript 的支持已经变得更加成熟。

React 的优点,和 Vue 的不足之处

与 Vue 相比,React 是一门领先的框架。这种领先有两个方面的表现:一方面,React 的生态系统更为丰富。

生态

实际上,生态系统和用户数量是相互促进的。用户越多,自然会涌现各种各样的解决方案。同时,也会发现和解决更多的 Bug,功能也会更加稳定。这种评价方法对 Vue 并不公平。Vue 的框架好坏不仅影响了使用体验,但这也是非常重要的一部分。Vue 的优势在于核心团队成员维护的相关项目做得很出色,而 React 团队成员的贡献有限。然而,React 的优势在于除了核心团队之外,还有更多优秀的团队在做更有趣的事情,并且做得非常出色。

正是因为有这么多的开发者,React 的生态百花齐放。有三种 CSS in JS 方案、好几种状态管理库、若干种路由策略。有人觉得这要逼死选择困难症,不像 Vue,官方已经把全家桶维护好了,生态非常统一。没错,这确实是 Vue 的优点之一。我发现 Vue 的优点是核心团队成员维护的周边项目都做得很棒,而 React 团队成员搞的都不了了之。然而,React 的优势在于,除了核心团队之外,还有更多优秀的团队在做更有意思的东西,并且完成得很好。

例如,React 生态中早就有了 Redux,而 Vue 的生态中则是后来才出现了 Vuex。React 生态中早早出现了 React Query 这样的 SWR 库,而 Vue 的生态则较晚,功能也较少。Next 和 Nuxt,虽然发布时间似乎差不多,但 Next 明显更加成熟。

尽管 Vue 的组件库方案在某些方面比其他框架好,但与 React 相比还存在很大不足。在 Vue 2 时代,我尝试使用 Element UI,然后迁移到了 Vuetify,但我不建议在任何情况下使用它们,即使它们的 GitHub star 数量比我所有项目加在一起还要多。使用体验远不如 Material UI、Mantine 等 React 框架。另外,目前 Naive UI 可能是一个较好的选择,但它几乎是一个个人项目。在 React 世界中,还有 Remotion 这种视频制作解决方案,还有 Framer Motion 这种顶级 FLIP 动画方案。总之,Vue 的生态系统只能说 " 足够用 ",而 React 的生态则丰富得多。

追随 Vue 的人可能会说,如果你需要某个功能,你可以自己开发一个,开源项目每个人都可以贡献。但当世界上已经有 React 供选择时,为什么要在 Vue 生态中重新开发一个相似的功能呢?仅仅是为了为 Vue 生态贡献?这个动机可能不够。特别是对于一些复杂的库,例如 React Native,目前在 Vue 生态系统下几乎没有替代品。未来,可以预见 Vue 可能不会有独立团队来维护这样的项目。我不想说如此消极的话,但就生态系统而言,Vue 很难赶上 React。即使 Vue 有一个新的概念,也会很快被引入到 React 生态系统中,可能会做得更好。反之却难以期待。

另一个领先之处在于,React 的思想似乎更为超前。例如,React Hook 是 React 框架的核心特性之一,它使函数组件可以拥有与类组件相同的状态和生命周期。然后才有了 Vue 的组合式 API,实现了类似的关注点分离,同时保持对原有 Vue 开发者友好。

此外,最近 Next.js 13 的发布,使服务器组件已经可以投入使用。虽然有人认为服务器组件似乎是回到了 PHP 的时代,但了解或使用过的人应该能感受到服务器组件是先进的,未来光明的。就像 SWR 技术可以在一定程度上替代 Redux/Vuex 一样,服务器组件的推广可能会改变前端,埋葬 SPA 应用程序,甚至埋葬 SWR。我认为,作为一个 Vue 爱好者,现在是时候开始着手服务器组件的研发或准备了,就像过去接受 TypeScript 和类似 Hook 的组合式 API 一样。不要被外部舆论束缚,不要一味模仿。不仅仅是要让我的用户使用 React 的优秀功能,我认为这是理所应当的。

组件化项目构成

另一个我认为 Vue 不足的地方是单文件组件。一个文件只能包含一个组件,这在某些情况下可能不够方便。特别是在存在一些小型、相关的组件时,需要在各自的.vue 文件中定义它们可能会显得有些冗余。这常常导致一个项目中的文件数量快速增加,特别在大型项目中,可能会使文件结构变得更加复杂,难以查找和管理特定的组件。

此外,尽管 Vue 的单文件组件在应用开发中非常方便,但在库或框架开发中却不那么方便。如果查看一些组件库的实现,会发现它们经常使用 JSX/TSX 或渲染函数,这可能会显得尴尬,说明模板的表现力可能真的不够强,复杂的功能无法很好地实现。Vue 和 JSX 支持的特性不同,编程体验非常不统一。我对在 Vue 中使用 JSX 有些抵触,因为这似乎不是官方推荐的方法,文档中也只有很短的一章介绍。在 Vue 中使用 JSX 的人不多,因此可能会面临更多问题。

说实话,React 官方推荐使用 Next 作为开发方案,而 Nuxt 也将成为 Vue 的首选生产环境开发方案。因此,Nuxt 是值得关注的。目前,Nuxt 还处于一个百废待兴的状态。在各种地方都会遇到 Bug,比如我最近尝试使用 Nuxt 搭建博客,就遇到了一系列问题,例如替换默认 Image 样式时会出现 Bug,设置页面转场动画后,点击其他链接时动画会出现 Bug,使用 Content 插件时,文章的路径文件夹不能包含大写字母,导致无法使用 zh-CN 这样的路径,从 Content 返回主页时,标题没有正确改变等。你会发现这里有许多功能是 Next.js 没有的,Nuxt 做了更多,所以出现 Bug 的机会更大。不过好在发现问题后,你可以提出问题并得到快速回应。短短两个周末,我已经合并了超过 10 个 PR,成为了 "Nuxter"。

总结

虽然有点像反串,但我真的这么觉得:在世界上最热门的框架,和第二热门的框架(之一)之中进行选择,也许选择第二热门的会更好。第一个理由是,这个领域 React 是当之无愧的 Top 1,其实许多人只是因为 Top 1 比较出名才选择了它。而拒绝 Top 1 选择 Top2 终究是要带点热爱的,如果你没有使用过,你也可以相信它是真的有独到之处,等待着被你发现。第二个理由是,当你愿意给开源项目做贡献时,你会发现给 Vue 生态添砖加瓦比给 React 提意见反馈明显得多。一方面,生态上值得去做的东西很多。因为许多用 React 实现的小的功能都可以用 Vue 实现,并且实现得可能还更快。

总结起来,我认为 Vue 比起 React 仍有许多不足。因此我会建议在生产环境中,更优先选择 React,考虑到它的灵活性,成熟的生态系统,以及高效的开发模式。但在个人学习的旅程中,如果你有足够的热情和精力,那么完全可以尝试 Vue,它有着足够的影响力,你可以让它变得更好。

Footnotes

  1. https://zh-hans.react.dev/learn/you-might-not-need-an-effect
#Vue #React
本文采用 CC BY-NC-SA 4.0 协议进行公开。