共计 4027 个字符,预计需要花费 11 分钟才能阅读完成。
大家好,我卡颂。
前几天,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
)的肩膀上。
这是不是说,我还是比伟人要高呢?