为什么尤雨溪说react的性能不如vue?
Posted SHERlocked93
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么尤雨溪说react的性能不如vue?相关的知识,希望对你有一定的参考价值。
作者是前Facebook/Meta前端工程师,也是近4000颗星react-native-web的作者。
此文摘自Andy Lee:React Turbo - React Forget 殺手[1]
React Turbo - React性能的最终救星
道歉一下,我原文中把react-native-webrtc[2]写成react-native-web,若有误会的读者,质疑我一次我道歉一次。
先讲结论,React Forget救不了React,React再不转向以subscription(订阅式响应)为主的reactivity响应,还固守comparison(比较式响应),迟早会被Solidjs、Svelte甚至是Vue干掉。
React之殇
React千好万好,只有一点不好:开发者需要自己确保性能。
不知道各位React开发者有没有想过,为什么有useMemo, useCallback, React.memo这些API,甚至useContext这个原本是用来放store, theme这种全局(considered “global” for a tree of React components)[3]的hook也被拿来提升性能,而最近的useEvent也几乎只是为了性能才增加。最惨的是就算用了这些方案,依然解决不了React的性能问题。
这才导致最近三年在redux, mobx后,居然还能出现引起浪潮的Recoil, zustand, jotai状态管理框架,而他们毫无例外都是subscription-based reactivity(订阅式响应)。
Subscription reactivity 订阅式响应 vs. Comparison reactivity 比较式响应
举个栗子,有一个text状态在组件A,组件A把text传给组件B,组件B把text传给组件C,以此类推,组件Y把text传给组件Z,最后组件Z上的。
function A()
const [text, setText] = useState(''');
return <B text=text />
function B(text)
return <C text=text />;
// ...
function Z(text)
return <input value=text />;
当text变化时,组件A,B,C,D...到Z全部都会re-render重新渲染,而这是Comparison reactivity 比较式响应永远都避免不了的。
而Solidjs、Svelte, Vue甚至Angular会说:啊?为什么A到Z都要重新渲染?只要Z甚至只要这个input渲染就好啦
这时候你或许会说,用Context啊!然而就像Recoil介绍的视频[4]说的,当text不是一个,而是一个数组或nested的值时,Context是帮不上忙的。
Comparison reactivity 比较式响应的问题
只要React继续固守比较式响应,那
React永远都达不到 fine-grained reactivity(细粒度响应)。
为了避免父组件们,与同组件内部的无关的value(值)与element(元素)被重複但无意义计算,都需要memoize(compare to return cached or new value)。
function Todo(todos, showDone, text)
const filteredTodos = todos.map(todo => todo.done === showDone);
return (
<div>
<ul>filteredTodos.map(todo => <li>todo.task</li>)<ul>
<input value=text />
</div>
)
有人可以解释:为什么text变化时,filteredTodos要重新计算呢? 你可能会回:加个useMemo就好啦!(依然有compare的无意义消耗)
那为什么<ul>...</ul>
要回传新的React.createElement ,然后会造成Virtual DOM要重新但无意义比较呢? 你可能会回:Virtual DOM很快的。
然而<ul>...</ul>
通常不是一个元素,而是一群组件,而这样组件又有包含一群组件。而他们的value和element 都必须重新创建,然后通常都很幸运的被浅比较,就发现相同,所以不会回传新值或去改真的DOM。
然而这些overhead消耗,
在大型或深deeply nested元素网页
有长list或table
输入是高频率,如text input文字输入、slider/range input拖动输入,或是如React Forget展示的color picker拖动式颜色输入,动辄每秒30-100ms一个event
已经证明,原本看来是微不足道的comparison overhead消耗,已经真正影响性能,使用者介面会有可体验的延迟。
然而,这些overhead消耗,在Subscription reactivity订阅式响应压根不需要考虑,因为订阅式响应就是指哪打哪,无关数值的创建和比较压根不会发生。
React Turbo
React Turbo是一个babel plugin。
https://github.com/oney/react-turbo[5]
会在compile time编译时把react组件中的值,从普通的javascript值,包一层成为可观察/可订阅observable/subscribable的signals/atoms,就像是mobx, recoil, solidjs的state
并且以element为单位(而非component为单位)来订阅这些值。
理论上可以达到跟solidjs[6] 同等级的响应速度和低消耗。並且依然是immutable values相容於concurrent mode。
并且最重要的一点是,开发者完全不需要改动现有的代码,编译器会do the magic。
你可能好奇,为什么我说了这么多,现在才提到标题中的React Turbo?
虽然说React Turbo目前可运作(但有bug要修),但要真正达到最高性能,需要React核心有所改动来配合。再说我一个普通的开发者,没有利益,也不该是我的责任去优化React。
React Turbo比较是一个proof of concept,告诉React team:React变成真正的Subscription reactivity 订阅式响应是可能的。
未來
你敢想像一个没有useMemo, useCallback, React.memo, useEvent,甚至不需要写reselect的createSelector,也不需要再学乱七八糟mobx, recoil, zustand的世界吗?
React有过他辉煌的岁月,就像Facebook曾经很酷,Jquery曾经很酷,React曾经很酷过,但他现在老了落后了。
React再不革新,虽然最终慢慢死去,但在这过程中会继续折磨著因为生态、工作、已有代码而留存的开发者。
给自己一次机会吧,React。一个深爱React五年以上的coder笔。
参考资料
[1]
Andy Lee:React Turbo - React Forget 殺手: https://zhuanlan.zhihu.com/p/530458257
[2]react-native-webrtc: https://github.com/react-native-webrtc/react-native-webrtc
[3](considered “global” for a tree of React components): https://zh-hant.reactjs.org/docs/context.html#when-to-use-context
[4]Recoil介绍的视频: https://www.youtube.com/watch?v=_ISAA_Jt9kI
[5]https://github.com/oney/react-turbo: https://github.com/oney/react-turbo
[6]solidjs: https://www.zhihu.com/search?q=solidjs&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2533826292%7D
最后
如果你觉得这篇内容对你挺有启发,我想邀请你帮我个小忙:
点个「喜欢」或「在看」,让更多的人也能看到这篇内容
我组建了个氛围非常好的前端群,里面有很多前端小伙伴,欢迎加我微信「sherlocked_93」拉你加群,一起交流和学习
关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。
点个喜欢支持我吧,在看就更好了
以上是关于为什么尤雨溪说react的性能不如vue?的主要内容,如果未能解决你的问题,请参考以下文章