服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - 编程技术 - Angular 之父为什么怼 React ?

Angular 之父为什么怼 React ?

2023-05-10 16:18魔术师卡颂卡颂 编程技术

前几天,Angular之父「Miško Hevery」和「Dan」在推上发生了一段有趣的对话,对话背景大概是:传统SSR(服务端渲染)场景下使用的技术叫Hydration,「Miško」曾向「Dan」演示了一个新技术概念 —— Resumable。「Dan」认为这项技术不可行

Angular 之父为什么怼 React ?

大家好,我卡颂。

前几天,Angular之父「Miško Hevery」和「Dan」在推上发生了一段有趣的对话,对话背景大概是:

  1. 传统SSR(服务端渲染)场景下使用的技术叫Hydration,「Miško」曾向「Dan」演示了一个新技术概念 —— Resumable。
  2. 「Dan」认为这项技术不可行。

Angular 之父为什么怼 React ?

  1. 「Miško」在Qwik框架中实现了Resumable。
  2. 「Dan」表示在React中我们之所以没有考虑Resumable,并不是因为框架不好接入,而是因为Resumable并不是更优解。

Angular 之父为什么怼 React ?

  1. 「Miško」表示这是吃不到葡萄说葡萄酸。

Angular 之父为什么怼 React ?

那么,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个步骤,只有在整个流程完成后应用才能响应交互:

  1. 下载HTML。
  2. 下载所有JS文件。
  3. 解析、执行JS文件(主要是框架及其依赖,还有业务逻辑代码)。
  4. 绑定事件(即hydrate操作)。

Angular 之父为什么怼 React ?

图来自于qwik文档

在某些应用场景(比如电商、博客)下,除了第一步,其他步骤可能不是必须的。

比如,对于一个电商商品详情页,除了展示商品所需的HTML外,其他都不是首屏渲染所必须的。

这就是Qwik框架中Resumable技术的设计理念 —— HTML优先,JS按需下载:

Angular 之父为什么怼 React ?

图来自于qwik文档

要实现Resumable,需要抛弃传统框架以CSR为基础(用JS生成HTML为主)的思路,转而以SSR为基础(以服务端生成HTML为主),再在此基础上附加CSR功能。

为什么叫Resumable?

Resumable的理念概括起来就是「按需下载、执行JS」。

所有JS代码的下载及运行会延迟到需要的时候再执行。在如下官方示例1[3]中,会渲染一个按钮,「按钮的点击回调对应代码」不会在首屏渲染时下载:

  1. export default component$(() => { 
  2.   return ( 
  3.     <button 
  4.       onClick$={() => { 
  5.         // 这部分代码不会在首屏渲染时下载 
  6.         console.log('click'); 
  7.         const div = document.querySelector('#container')! as HTMLElement; 
  8.         div.style.background = 'yellow'
  9.       }} 
  10.     > 
  11.       执行 
  12.     </button> 
  13.   ); 
  14. }); 

只有在点击按钮时,对应代码才会被下载并执行:

Angular 之父为什么怼 React ?

这就使得首屏渲染时需要下载及执行的JS文件大大减少,提高了FCP及TTI指标。

实际上,如果以Chrome lighthouse的评分作为评判依据,其他框架确实都难以望Qwik的项背

这项技术之所以叫Resumable(恢复),是因为它与传统Hydration技术在首屏渲染时客户端逻辑的区别。

传统Hydration技术在首屏渲染时,客户端(比如浏览器)会全量执行框架代码与业务逻辑代码,并在此过程中完成:

  • 框架组件对应的树状数据结构初始化(比如在React中叫Fiber树,在Vue中叫VNode树)
  • 组件内状态初始化
  • 事件绑定

而以上过程在Resumable技术中是发生在服务端的。比如,对于上述按钮的例子,点击回调对应的下述代码会在服务端生成HTML时完成序列化:

  1. onClick$={() => { 
  2.   console.log('click'); 
  3.   const div = document.querySelector('#container')! as HTMLElement; 
  4.   div.style.background = 'yellow'
  5. }} 

序列化后的数据会以HTML属性的形式存在:

Angular 之父为什么怼 React ?

当点击事件发生后,框架的前端部分会根据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从数据库取不同数据,再将数据传递给子组件(客户端组件):

  1. function ServerCpn({id}) { 
  2.   const data = db.get(id); 
  3.   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技术的优越性:

Angular 之父为什么怼 React ?

在这里我们不比较技术优劣。只是说单纯用脚投票,除了React外,确实有很多框架都使用了Signal相关技术,比如:

  • Vue
  • Preact
  • Qwik
  • 新版Angular
  • Solid.js

在「Miško」看来,React团队之所以不采用更优秀的技术,是由于一旦采用新技术,就没法完美的向后兼容,势必造成社区生态的割裂。

作为Angular的作者,「Miško」对这种后果再清楚不过了。

Angular 之父为什么怼 React ?

但是,React团队却认为 —— React之所以没有采用这些技术,是因为自身的技术路线更优秀。

Angular 之父为什么怼 React ?

这里「Dan」举出的例子是Hooks和RSC。

本文已经做过RSC与Resumable的比较。在笔者看来,两者是不同技术路线(CSR优先还是SSR优先)下的优秀代表。

但就Hooks而言,笔者认为Hooks优秀在其理念,而不是实现。同样基于Hooks理念实现的Vue Composition API在使用体验上比React Hooks更佳,比如:

  • 没有闭包陷阱
  • 没有显式指明依赖的心智负担

之所以同样理念的不同实现使用体验不同,完全是由于底层的技术实现区别造成的(这里指「底层变化监测方式」)。

所以,从这个角度想,笔者并不赞同React团队的说法。

我想,这也是为什么「Miško」会认为React团队吃不到葡萄说葡萄酸。

总结

大佬们的讨论总是理性、互相尊重且克制的。「Miško」在后续也表示了自己对React的误判。

Angular 之父为什么怼 React ?

在Qwik v1.0发布时,「Dan」第一时间送上祝福。

Angular 之父为什么怼 React ?

有意思的是,对于「Dan」的祝福,「Miško」回复道:我们都站在巨人(指React)的肩膀上。

Angular 之父为什么怼 React ?

这是不是说,我还是比巨人要高呢?

参考资料

[1]FCP:https://web.dev/fcp/。

[2]TTI:https://developer.chrome.com/docs/lighthouse/performance/interactive/。

[3]官方示例1:https://qwik.builder.io/examples/introduction/runtime-less/。

原文地址:https://mp.weixin.qq.com/s/MSQcDUyOHZLs9xNn6Nob4g

延伸 · 阅读

精彩推荐