大家好,我卡颂。
前几天,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[1](First Contentful Paint)与TTI[2]指标(time to interactive)。
下图展示了SSR
场景下hydrate
的流程,包括4个步骤,只有在整个流程完成后应用才能响应交互:
- 下载
HTML
- 下载所有
JS
文件 - 解析、执行
JS
文件(主要是框架及其依赖,还有业务逻辑代码) - 绑定事件(即
hydrate
操作)
图来自于qwik文档
在某些应用场景(比如电商、博客)下,除了第一步,其他步骤可能不是必须的。
比如,对于一个电商商品详情页,除了展示商品所需的HTML
外,其他都不是首屏渲染所必须的。
这就是Qwik
框架中Resumable
技术的设计理念 —— HTML
优先,JS
按需下载:
图来自于qwik文档
要实现Resumable
,需要抛弃传统框架以CSR
为基础(用JS
生成HTML
为主)的思路,转而以SSR
为基础(以服务端生成HTML
为主),再在此基础上附加CSR
功能。
为什么叫Resumable?
Resumable
的理念概括起来就是「按需下载、执行JS」。
所有JS
代码的下载及运行会延迟到需要的时候再执行。在如下官方示例1[3]中,会渲染一个按钮,「按钮的点击回调对应代码」不会在首屏渲染时下载:
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
的主要区别其实体现在框架底层实现层面。