如果有看过我的博客的话,应该会知道我一直都是写 React,完全没有碰过 Vue,也没有碰过 Angular。自从 2015 年接触到 React 后,工作上就一直是用 React 了。
然而,最近因为工作上的需求,所以开始写 Vue 了,而刚好也有读者来问我从 React 跳到 Vue 的心得,因此这边就简单写一篇来分享。
在开始之前…
虽然说要讲从 React 跳到 Vue 的感想,但先让我偷渡一下对于 Next.js 13.4,也就是 app router 搭配 RSC(React Server Components)的感想。照理来说应该要开另外一篇的,但篇幅不够长,因此就偷渡在这里了。
如果没兴趣的话,可以直接跳到下一段。
在目前的公司,React 跟 Vue 都会碰到,而且版本都满新的,前者是 Next.js 14(刚用的时候是 13.4,第一个有 RSC 的版本),后者则是 Vue3。
因为都用了 Next.js 的最新版本,所以直上 RSC,想来体验这个 React 未来的重点技术之一,先讲结论:「不能只有我受苦,赶快来用」。
(话说如果还不清楚 RSC 是什么,或是容易跟 SSR 搞混的话,建议可以先看这两篇文章:RSC From Scratch. Part 1: Server Components 以及 Understanding React Server Components)
根据 RSC 的设计原则,如果运用得当的话,你的 bundle size 会变小,网站的性能也可能会变好,但我自己用过之后,认为它带来的效益远低于引进这项技术所增加的复杂度。
不过先强调一下,因为我用的是 Next.js 的 RSC,不代表所有的 RSC 都是同个样子,所以这整段讲的都会是「Next.js 的 RSC 的使用心得」,而不是「RSC 的使用心得」。
先来讲缺点好了。
首先,光是要正确理解 client component 跟 server component 就需要一些时间,可能是尝试的时间太早,甚至连 Next.js 的官方文档都写得不是很清楚,需要自己一直不断尝试才能试出来到底是什么样子(例如说之前前端社群有一篇贴文就在问这个,我当初也有类似的疑惑)。
再来的话,未来在写 component 的时候都会需要考虑到这个是 client 还是 server 还是都可以,会增加心智负担。
还有就是许多 server component 可能会直接打 API 去拿数据,因此 client 在拿到数据时,就已经是 render 好的结果了。虽然乍看之下不错(毕竟是 RSC 的卖点),但这其实会让前端变得很难 debug。
以前除了第一次的 SSR 以外,我只要打开 DevTools,就可以看到前端发了哪些请求,API 的 response 是什么,但换成 server component 以后我看不到了,我只能看 server log 才能知道发生了什么事情。
如果出事的话,我从前端没办法区分出是我的 Next.js server 出错,还是我调用的 API 那边出错,这点在开发者体验上扣分许多。
但以上这些其实都还好,最雷的是 Next.js 13.4 的推出有点太赶,要嘛很多功能都没有做好,要嘛是文档没有写清楚。
举例来说,Next.js 有一个叫做 middleware 的东西,很直觉就会理解成是一个在处理 request 之前会执行到的文件。但文档没有写清楚的是,这个 middleware 跟你其他的程序码,是跑在不同的执行环境的(现在我记得已经有补上了,Next.js 的改版也满勤快的就是了)。
也就是说在 middleware 里面写一个 global.a = 1,你到 Next.js 的 server component 里面 log 出 global.a,答案会是 undefined。
再者,middleware 并不是跑在完整的 Node.js 环境上面,而是跑在一个叫做 Edge Runtime 的地方,有许多的功能跟 API 都不支持。
之所以这样搞,是因为 Next.js 缺省了这个 middleware 就是要跑在 edge 上,就算我们根本不会用到 edge 这个功能也一样,而且目前依然没办法改变这点,更多讨论可以看这一串:Switchable Runtime for Middleware (Allow Node.js APIs in Middleware) #46722。
顺带一提,我目前完全不支持把 Next.js 当一个全栈框架来用,也就是前后端专案全都挂在 Next.js 上,理由很简单,那就是它本来就不适合这样用。Next.js 它所提供的 server 目前更像是 BFF(Back-end For Front-end),可以当作前端跟其他后端的桥梁,但没办法自己实作出完整的功能(除非你的专案很小,功能很少)。
如果真的把后端功能搬到 Next.js 上,那注定是场悲剧。
讲完了缺点,来讲讲优点,那大概就是 bundle size 真的有小一点。例如说 i18n 好了,以往没有特别做什么调整的话,大部分的 client 都会下载到「超出目前使用范围以外」的字符串,例如说所有的中文本串,或至少是当前 namespace 底下的字符串。
但用了 RSC 以后,由于 server component 的 i18n 在 server 直接做掉了,所以这部分就不需要下载任何额外的字符串。
除此之外,其实我没体验到太大的好处(而且因为公司专案的一些特性,在搭配上同时有 client 跟 server component 需要考虑,现有的 i18n 套件每一个都有问题,只好自己简单做了一套)
总之呢,我个人是不太推荐使用 app router 的,带来的效益远低于导入的成本,还会把很多事情弄得更复杂。我是从去年七八月就开始用 Next.js 13.4 了,那时候的状况更糟,文档跟程序码的行为配对不上的事情也发生过。
如果有人跟我说 Next.js 13.4 以后的 app router 超好用,那我会觉得要嘛是用得不够多,要嘛是专案很小,所以没有体验到坏处,更何况我都还没讲那一堆缺省开启而且有些关不掉的缓存策略。
以上就是偷渡的 Next.js RSC 心得,因为从去年七八月就开始用了,其实刚用的那两三个月最有感,真的很多点可以吐槽,但现在已经有点忘了,我也害怕想起来。
从写 React 转去写 Vue 的感想
话说这篇会尽量写的是 React 与 Vue 本身的心得,而不是特定的函式库或框架。
举例来说,如果我原本在 React 都是用 Redux,转到 Vue 之后用 Pinia,然后写说:「哇,写 Vue 真的太棒了啦,Pinia 好简洁好好用,比 React 好太多了」,这个论述是有问题的,因为在 React 圈其实也有类似的 zustand 可以用。
所以这一句在比较的主体并不是 Vue 与 React,而是 Redux 与 Pinia,变成了特定函式库的比较,这是这一篇想要避开的论述。
不过为了补充脉络,还是先把这些函式库与框架稍微讲一下好了,React 的话目前我的起手式大概就是 Next.js 搭配 Zustand 搭配 tailwind,而 Vue 的话就是 Nuxt 搭配 Pinia 搭配 tailwind。
以使用体验来说,我觉得两个是差不多的(如果 Next.js 是 page router 的话),所以这部分就不多提了。
再来,使用的感想会与使用经验多寡以及应用的专案有差,目前手边大约有 4 个内部的中小型专案都用到 Vue,我写 Vue 大概写了四个月左右,其实也没有很长,另外因为是内部工具,所以都没有开启 SSR,直接走纯 client side render。
讲完了这些前提以后,接着就来讲讲使用的感想,先来讲我自己比较喜欢 Vue 的地方。
先讲一下状态管理的部分。
首先是 Vue 的双向绑定真的满香的,v-model 真的好用。以往在 React 都是 value + onChange 都写,现在用 v-model 一行就搞定了。
而差异最大的我觉得在于 useEffect。在 React 中需要大量用到 useEffect 去处理一些事情,然后要考虑到 dependency 以及各种状况,一不小心就可能写坏。
但是在 Vue 中就没有这种困扰,省了很多心智负担,你要写坏其实满难的。
而这个特性的差异,也让我对于专案的技术选择多了一个思考的维度,那就是「下限」。以前我在思考技术时,比较容易思考到「一般的使用状况」,像是我写 React 写久之后,其实不会特别觉得 useEffect 有什么,写得也算是顺手。
但同时我也承认 useEffect 是一个需要经验才能写好的东西,有一定的学习门槛,这也表示它的下限可以很低。写得烂的工程师,可以写一堆 useEffect 然后 dependency 乱写却维持一个恐怖平衡,东西刚好可以动。若干年后如果我去接手,我会不知道从何改起,因为只要一往里面加东西,就是整个坏掉,而且还是多个 effect 一起坏掉。
但我自己觉得 Vue 就不同了,你写得再怎么烂也就那样了。同样都是一个技术能力很差的人来写,他所写的 Vue 会比 React 好维护,我是这么认为的,这就是我所说的「下限」。
那如果现在有个新的团队,里面都是前端超级新手,他们写的专案你过半年之后要维护,已经可以预期到维护性可能会较差的情况下,选择下限比较高的 Vue 似乎会比较好,至少你改得动。
而另外一个也是从团队出发的角度是「上手难度」,如果团队内的人手比较不足,前后端要互相支持的话,那 Vue 也是个会比 React 更好的选择,因为更好入门,所以就算不熟悉前端也能够快速上手。
总之呢,从状态管理来看的话,我觉得 Vue 更直觉也更好上手一点,而 React 的话确实是比较复杂。
接着来谈 render 的方式,React 就是 JSX 一路到底,整个 component 就是一个 function,里面是 JSX。而 Vue 的话则是把 template 跟 functional 分开,我觉得两者各有其优劣。
对于一些需要 early return 的状况,例如说如果是加载中就只显示 loading,React 我觉得会更加直觉一点,就 component 看个前几行就知道了。而 Vue 的话则是 setup 的地方看完还要再回去看 template 才能确定。
除此之外,v-if 与 v-for 那些其实满好用的,而且 template 看起来也比较整齐,在结构没有相差很多的情况下可读性比较好。
优点讲完了,来讲一些缺点。
第一个缺点是在 props 的部分我觉得 React 更加直觉,就是 function 的参数而已,而 Vue 的话则是要额外定义,而且在传入的时候提倡的是 kebab-case,原本叫做 testProps 要改成 test-props,我自己不是很喜欢这样,因为两者不一致的话会导致搜索有点困难。
虽然说我看文档也是可以用 testProps,但官方文档提倡的作法依然是 test-props。
第二个缺点是一个文件只能有一个 component,我觉得这个满不弹性的,会容易出现一大堆小的文件。虽然以前也有人在 React 中提倡这种做法,一个文件一个 component,但我认为那是不好的,因为有些 component 如果不能被其他组件重用,那就应该放在同个文件,比较好找也比较好维护。
不过这点似乎也可以解决,我有查到相关的方法:
这样看下来,好像我上面提的两个缺点其实都有方法可以解决,纯粹是我之前对 Vue 不够熟所以不知道而已,之后再来试试看。
总结
以上就是我对使用 Next.js 13.4 app router + RSC 的心得,以及从写 React 转到写 Vue 的心得。
总之呢,感想大概就是 Vue 确实简单好上手,但还需要再观察一阵子,毕竟 code 写得越多才会越有感觉,像我这种只写了三四个月的,通常还在甜蜜期,只体验到好处而非坏处。当写的程序码愈多,专案也愈复杂的时候,应该就会遇到一些之前没碰过的问题。
或许要再写个一两年才会有更多心得吧?不知道那时候的前端会长成什么样子。