大家好,我卡颂。
前几天,Angular
之父Miško Hevery和Dan在推上产生了一段乏味的对话,对话背景大略是:
- 传统
SSR
(服务端渲染)场景下应用的技术叫Hydration
,Miško曾向Dan演示了一个新技术概念 ——Resumable
- Dan认为这项技术不可行
- Miško在
Qwik
框架中实现了Resumable
- Dan示意在
React
中咱们之所以没有思考Resumable
,并不是因为框架不好接入,而是因为Resumable
并不是更优解
- Miško示意这是吃不到葡萄说葡萄酸
那么,Resumable
到底是什么技术?他和React
在推动的RSC
(React Server Component
)有什么区别?Miško为什么会作出上述舆论?
让咱们通过本文理解一下。
欢送退出人类高质量前端交换群,带飞
Resumable(复原)是什么
Resumable
的概念源于一次思路的转变。
尽管支流前端框架都反对SSR
,但不论是React
、Vue
还是Angular
,他们都是CSR
(客户端渲染)优先。
在这些框架中,SSR
是在CSR
的根底上附加的新性能。
正是因为传统前端框架都是CSR优先的产物,才导致一些常见SSR
问题,比方:
- 首屏渲染时,页面短时间无奈响应交互,因为此时框架还未
hydrate
实现 - 即便仅有局部内容须要交互,但整个页面还得全量
hydrate
这些问题拉低了SSR
场景下的FCP(First Contentful Paint)与TTI指标(time to interactive)。
下图展现了SSR
场景下hydrate
的流程,包含4个步骤,只有在整个流程实现后利用能力响应交互:
- 下载
HTML
- 下载所有
JS
文件 - 解析、执行
JS
文件(次要是框架及其依赖,还有业务逻辑代码) - 绑定事件(即
hydrate
操作)
在某些利用场景(比方电商、博客)下,除了第一步,其余步骤可能不是必须的。
比方,对于一个电商商品详情页,除了展现商品所需的HTML
外,其余都不是首屏渲染所必须的。
这就是Qwik
框架中Resumable
技术的设计理念 —— HTML
优先,JS
按需下载:
要实现Resumable
,须要摈弃传统框架以CSR
为根底(用JS
生成HTML
为主)的思路,转而以SSR
为根底(以服务端生成HTML
为主),再在此基础上附加CSR
性能。
为什么叫Resumable?
Resumable
的理念概括起来就是按需下载、执行JS。
所有JS
代码的下载及运行会提早到须要的时候再执行。在如下官网示例1中,会渲染一个按钮,按钮的点击回调对应代码不会在首屏渲染时下载:
export default component$(() => { return ( <button onClick$={() => { // 这部分代码不会在首屏渲染时下载 console.log('click'); const div = document.querySelector('#container')! as HTMLElement; div.style.background = 'yellow'; }} > 执行 </button> );});
只有在点击按钮时,对应代码才会被下载并执行:
这就使得首屏渲染时须要下载及执行的JS
文件大大减少,进步了FCP
及TTI
指标。
实际上,如果以Chrome lighthouse
的评分作为评判根据,其余框架的确都难以望Qwik
的项背
这项技术之所以叫Resumable
(复原),是因为它与传统Hydration
技术在首屏渲染时客户端逻辑的区别。
传统Hydration
技术在首屏渲染时,客户端(比方浏览器)会全量执行框架代码与业务逻辑代码,并在此过程中实现:
- 框架组件对应的树状数据结构初始化(比方在
React
中叫Fiber
树,在Vue
中叫VNode
树) - 组件内状态初始化
- 事件绑定
而以上过程在Resumable
技术中是产生在服务端的。比方,对于上述按钮的例子,点击回调对应的下述代码会在服务端生成HTML
时实现序列化:
onClick$={() => { console.log('click'); const div = document.querySelector('#container')! as HTMLElement; div.style.background = 'yellow';}}
序列化后的数据会以HTML属性
的模式存在:
当点击事件产生后,框架的前端局部会依据HTML属性
(示例中的on:click
属性)向后端申请具体的JS
代码(即点击回调对应的代码)并执行。
一句话总结就是 —— 在Resumable
技术中,所有以SSR
为主,局部在SSR
时未实现的操作(比方交互逻辑对应代码)会在须要触发时(比方交互产生时)再复原执行,所以这一技术叫Resumable
(复原)。
与RSC的区别
同样是SSR
相干技术,React
团队主导的RSC
(React Server Component
)与Resumable
有什么区别呢?
在解说他们的区别前,咱们要先理解一个背景常识:React
是CSR优先的框架,而且他曾经呈现很多年了(13年问世)。
尽管这些年呈现了很多优良的框架技术(比方Signal
、AOT
),但React
始终保持这套重客户端运行时技术架构。
在公布React Hooks
后,React
团队逐步将重心转移向服务端。因为其技术架构偏差客户端运行时,所以将React
间接革新为SSR优先显然不事实。
为此,React
团队的策略是 —— 提供SSR
能力,再让其余SSR优先框架接入(次要是Next.js
)。
所以,Resumable
与RSC
的次要区别其实体现在框架底层实现层面。
区别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
)的肩膀上。
这是不是说,我还是比伟人要高呢?