区别1:序列化方式
最大的区别体现在「序列化数据」方式的不同。
在Resumable
技术下,SSR
时会将大量数据序列化为HTML
属性或注释,比如:
DOM
与Qwik
组件的关系- 状态(是的,状态都会在服务端序列化为
HTML
属性,再在客户端恢复) - 代码逻辑(比如上述示例中的点击回调逻辑)
服务端完成了大部分工作,客户端需要做的仅仅是按需反序列化数据,并执行对应逻辑。
在RSC
中,服务端组件会被序列化为一种自定义JSX
协议,并被流式传输。之所以没有被序列化为HTML
字符串(就像Resumable
那样),是因为数据被反序列化后并不直接是HTML
,而是JSX
,JSX
经由React
处理后才会映射到HTML
,这么做能保持服务端组件的子孙客户端组件不丢失状态。
比如如下RSC
,根据id props
从数据库取不同数据,再将数据传递给子组件(客户端组件):
function ServerCpn({id}) { const data = db.get(id); return <ClicentCpn {...data} />; }
当id props
变化后,ClicentCpn
组件内的状态并不会丢失。就是因为服务端传输来的ServerCpn
是一种自定义JSX
协议,而不是HTML
字符串。
区别2:变化监测方式
通过区别1可以发现,RSC
中序列化的数据描述的是组件级别的内容(JSX
描述组件)。
而Resumable
中序列化的数据粒度更细(比如描述点击事件的回调逻辑,或者某个状态)。之所以会有这种区别,是因为两个框架采用不同的变化监测方式。
当状态变化后,React
需要遍历完整的组件树才能计算出「状态变化产生的影响」。所以序列化数据只需要描述组件级别的内容就行。
而Qwik
(实现Resumable
技术的框架)使用Signal
监听状态变化,这使得他能精确定位「状态变化所产生的影响」,即精确定位状态变化需要反序列化哪些数据。
区别3:后续的发展
由于React
是重客户端运行时的框架,所以虽然RSC
是SSR
技术,他的后续发展还是会与重客户端运行时的技术绑定(比如Suspense
、Selective Hydration
)。
Resumable
是重服务端技术,所以后续发展应该会围绕服务端展开,比如:
- 支持更多类型数据的序列化(当前不支持
class
序列化) - 支持序列化数据的流式传输
- 支持对「是否序列化数据」更精细的控制
Miško的想法
了解了这些技术细节,让我们回到开篇,为什么「Miško」会怼React
呢?
实际上,这并不是「Miško」第一次对React
发表看法。之前「Miško」就曾表示:即使React Forget Compiler
成功问世,他也没法解决props下钻
场景下的性能问题,并以此论证Signal
技术的优越性:
在这里我们不比较技术优劣。只是说单纯用脚投票,除了React
外,确实有很多框架都使用了Signal
相关技术,比如:
Vue
Preact
Qwik
- 新版
Angular
Solid.js
在「Miško」看来,React
团队之所以不采用更优秀的技术,是由于一旦采用新技术,就没法完美的向后兼容,势必造成社区生态的割裂。
作为Angular
的作者,「Miško」对这种后果再清楚不过了。
但是,React
团队却认为 —— React
之所以没有采用这些技术,是因为自身的技术路线更优秀。
这里「Dan」举出的例子是Hooks
和RSC
。
本文已经做过RSC
与Resumable
的比较。在笔者看来,两者是不同技术路线(CSR
优先还是SSR
优先)下的优秀代表。
但就Hooks
而言,笔者认为Hooks
优秀在其理念,而不是实现。同样基于Hooks
理念实现的Vue Composition API
在使用体验上比React Hooks
更佳,比如:
- 没有闭包陷阱
- 没有显式指明依赖的心智负担
之所以同样理念的不同实现使用体验不同,完全是由于底层的技术实现区别造成的(这里指「底层变化监测方式」)。
所以,从这个角度想,笔者并不赞同React
团队的说法。
我想,这也是为什么「Miško」会认为React
团队吃不到葡萄说葡萄酸。
总结
大佬们的讨论总是理性、互相尊重且克制的。「Miško」在后续也表示了自己对React
的误判。
在Qwik v1.0
发布时,「Dan」第一时间送上祝福。
有意思的是,对于「Dan」的祝福,「Miško」回复道:我们都站在巨人(指React
)的肩膀上。
这是不是说,我还是比巨人要高呢?