一些关于 React 和 Vue 的浅见
一些关于 React 和 Vue 的浅见
有感而发,写点关于 React 和 Vue 的见解。
React 有几个核心特点,单向数据流、不变性、可预测性、灵活性,以及最重要的——它足够简单。
是简单,不是易用,也不是绝对的简洁,并且也不意味着心智模型的简单。React 自 hooks 的引入之后让我感觉它最大的特点是简单,它的实现非常简单,不做太多优化,hooks 的实现也是只要有人点拨一下,你就知道这东西本质上就是外边有个数组,函数组件就是把函数整个跑一遍,这一切都不能更简单。要我说,实现 React 最核心的部分(不考虑优化)只需要不到 1000 行代码,一个更简单的核心版本甚至可能只要 300 行代码左右(当然,需要让你的代码变得很晦涩)——这甚至可能低于我们平常写一个 React 组件需要的代码行数……这听起来很疯狂,但 React 真的就是这么简单而已。
简单带来了可预测,带来了 React 极致的灵活,这使得在 React 生态中开发各种库非常简单,并且能够很轻易地集成到其他东西中,比如 RxJS 与 React 的相性就非常好。这都是简单带来的力量。
相应的,简单带来了更重的心智负担,毫无疑问。甚至直到今天,函数式组件是否比类组件的心智负担更小,都仍无一个能被大多数人认可的结论。但是毫无疑问,函数式组件更简单了,并且它的不变性更强——this 本身就是个不稳定的可变因素,this.state 和 this.props 是总在渲染周期之间不断变化,这往往造成异步方法的不稳定性,也因此人们往往保护性地首先解构 this.state 或 this.props,这也是 hooks 推出的一大缘由。
React 这种将一切作为函数的近乎魔怔的不变性狂热究竟好不好我们仍不得而知,但显然它很受欢迎。有人说这是因为大公司往往倾向于采用 React 以尽可能掌控渲染细节,而不那么在乎心智负担与开发效率,我则更倾向于是 React 的简单性造就了 React 生态的繁荣,也以此造就了 React 的繁荣与推广。
并且,JSX 的语法真的很容易给第一次看见它的人以某种震撼,而这是 Vue、Svelte 以及 Angular 的模板语法都做不到的——我们直接将 HTML 作为函数的返回值,这是多么疯狂又多么成功的创意。在人们的想象力仅局限在模板上的匮乏年代,这实在是太大胆也太令人振奋了。
当然,JSX 在日后引发了很多关于可维护性的讨论。JSX 在许多不那么注重可维护性的程序员手里经常呈现灾难性的后果——一堆三元运算符嵌套的谁都不认识,嵌套层级多到看的人想立刻紫砂,一堆回调直接写在 JSX 里看起来就像是一坨屎,以上种种都是我非常喜欢犯的错误。
不过这其实不完全是 React 自己的问题——由于 TS 的类型推导是单向的,将回调直接写在 JSX 里能获得最大程度的类型推断,可以少写很多类型而不用去类型定义文件里找来找去然后在外边定义个函数(我不得不说如果程序员懒于找到正确且精准的类型而是习惯于在外边定义个函数并安个 any,再把它放进 JSX 里,那比写里头还坏的多),我们当然会更喜欢直接在 JSX 里写回调函数。但是在具有双向推导的编程语言中这就很简单,ReScript 是个好例子,它作为编译到 JavaScript 上的一门 ML 方言,具有这样的能力,并且能够很好地与 React 集成,不过我没用过,只是口嗨。
至于 Vue,它的思路与 React 截然不同。尤雨溪当年的想法只是“我要做个自己用着舒服的框架”,所以 Vue 的逻辑很简单,就是“用着舒服”。基于“用着舒服”,我们引出了一些其他的特点,比如符合直觉(Intuitive)、编译期优化(程序员不用怎么操心性能问题)、模块化程度较高(大家的代码以大致相同的结构写,很清楚,并且大家只需要往框架里填内容)、零配置使用(是的,Vue 的所有 template 都是合法的 HTML,可以直接通过 CDN 引入然后直接开始写 HTML,什么都不用配,只是大家通常不这么做)、简单易学、响应式(这个和符合直觉对应)等。
Vue 2 尤其反映了这些特性,写在 data 里的属性就是默认响应式的,比如一个计数器上的值就是简单绑定到了 this.count,让它增加就是 this.count++,这的确是太符合直觉了,比起 React 使用 useState 达到的可变,这在心智模型上的确简单了许多。
同时,Vue 也倾向于在幕后做很多工作。Vue 的响应式很快,在默认情况下比 React 快得多,即使它使用了许多 Proxy,而这似乎会显著降低响应速度,但 Vue 团队总是默默地将优化问题自己担着,让用户最大程度地享受优化。我真的非常钦佩这个团队,相比于 React 团队将大量时间用于思考设计,Vue 团队思考设计的时间少了很多,他们更加务实,并且会选择直接动手开始以惊人的毅力攻克一个个难关,Volar 就是他们的一大成果,Vue 各种好用的 devtools 也是。这也与他们对社区的态度密切相关,从 vue-router 到 vuex 到 vite 到 vitest,Vue 团队总是帮你包办一切。
所以 Vue 追求另一个东西:易用性与直觉性。这意味着 Vue 背后的实现必然比 React 复杂得多,但是其心智模型理所当然要简单不少(当然,你也可以认为用 React 时你掌控的更多,心智负担更低,见仁见智)。并且 Vue 某种意义上也很简洁,事实上我认为比 React 更加简洁。而且 Vue 的思路带来更加模块化更可维护的代码,说人话就是,它让弱智程序员没法写出太弱智的代码。当然,如果弱智程序员偏要在 Vue 里用 JSX 还不知道自己代码写得很烂,那你也没辙。
而 Vue 3 某种意义上走向了一条稍微不同的路子,这一切都是因为 TS 的流行与 Vue 2 在集成 TS 上的困难。Vue 为了实现它的直觉性太依赖于 JS 的动态特性,而这其中许多与 TS 的相性很差。例如 Vue 2 一直在为 props 标注类型上很困难,自从 Vue 3 特别是 defineProps 和 withDefaults 出现后,这一切变得就非常简单了。说真的,大家应该真想不到在 TS 3.0 时期 Vue 2 要实现 this.data 的类型提示究竟是多困难,那就是在纯堆人力。
显而易见,Vue 2 由于与 TS 相性太差导致不再能持续维护,Vue 3 不得已被开发出来。包括 Vue 3 中的 setup、hooks 的引入等,某种意义上都是对 TS,甚至是对 VSCode 这样一个编辑器的妥协。
后来 Vue 3 的 setup 语法出现之后,Vue 的面貌焕然一新,Vue 3 加上 setup 的代码看起来甚至有点像 React,并且嵌套层数更少。不过,这种灵活性也带来了比较严重的可维护性的下降,也就是其对程序员的要求更高了。Vue 2 的一大特点就是其返回对象的语法很模块化也很好维护(模块化似乎不是一个合适的词,但我想不到一个更好的词了),可以让弱智程序员的下限提高,而 Vue 3,它提高了灵活性并提高了聪明程序员的上限,同时也显著降低了弱智程序员的下限,ref 和 reactive 写得到处都是成了常态,这样的代码某种意义上不如 Vue 2 好读。
当然,我仍然认为 Vue 3 的进步是我所喜欢的,我当然更喜欢灵活性,而不是让一个框架限制我的自由。但许多公司恐怕不这么想,他们拼了命地想提高弱智程序员的下限,因为弱智程序员们往往便宜好招。
而后来的一些框架,像是 Svelte,说实话这个在某种意义上让我觉得是一种倒退,它似乎有点 JSP 的影子在里边。$: 语法奇丑无比,那个 `