Reactive JavaScript:前端架构的演变

235 阅读9分钟

改善客户端的网络体验意味着要克服 "水合 "的挑战,这是一个迷人的工程问题,正在以许多不同的方式加以解决,让我们深入了解一下:

今天软件开发中最有活力的领域之一是前端架构。一些创新者正在推动技术的发展,以设计出更强大的方法来构建动态用户界面。这些工作大部分都是以迅雷不及掩耳之势发生的,而且是在公开场合。

由于一些开源的JavaScript项目,如SvelteKit、Solid、React、Qwik和Astro,我们有一个前排座位,可以看到未来网络的演变。这里有一份理解行动的指南。

什么是水化?

围绕改进现代前端架构的许多活动都集中在所谓的水合。为了理解什么是水合,以及为什么它是现代前端架构的核心,让我们掌握一下起作用的高级概念。为了实现反应性的奇迹,每个框架都必须处理下图所示的三个方面。

javascript reactivityIDG

反应性的高层次方面。

图中的基本信息是,框架负责框定视图,保持状态,并管理它们之间的交互。(如果你熟悉MVC模式,你会听到这里的呼应)。

一旦这三个部分到位,你就可以开始了。用户可以看到这个页面并与之互动。

天真的,或者说默认的方法是简单地把客户需要的所有东西--框架、反应式代码和状态--都发送过去。然后,客户端(浏览器)完成显示框架的工作(也就是绘制用户界面),解释JavaScript,并将状态连接起来。

这种方法有一个很好的好处,就是简单,无论是对工作中的代码还是对试图理解它的人来说都是如此。它也有一个很大的缺点。最初的页面渲染必须等待所有的东西,而用户则必须忍受所有的网络和浏览器的干扰。此外,除非小心谨慎,否则页面会倾向于显示,然后令人尴尬地重新排列成最终的布局。这不是一个好的外观。

这启发了开发者尝试先在服务器上渲染初始页面*(服务器端渲染*或SSR),然后将其发送过来。然后,用户有一个像样的页面可以看,而其余的代码和状态被发送和引导。这是一个很大的简化,但这是一个基本的想法。

让基本布局到位所需的时间被称为首次内容绘制 (FCP)。 页面需要达到的下一个里程碑是由交互式时间 (TTI)衡量的,意思是直到用户能够实际使用该页面的时间。

把最初的页面变成互动的过程--这就是水化

服务器端渲染的局限性

底线是,SSR倾向于改善FCP,但恶化了TTI。因此,我们的目标是在这两者之间取得平衡,同时最大限度地提高这两者,同时希望保持愉快的开发者体验(DX)。

在改善水合的努力中,各种方法被提出、采用、放弃、修改和组合。一旦人们开始关注实施细节,就会惊讶于它变得如此复杂。平衡地提高FCP和TTI,并有一个体面的DX?听起来很容易,但事实并非如此。

造成这种复杂性的原因之一是,我们正处于对所有权衡的整理过程中;这是一个正在展开的场景。然而,一旦前进的道路变得具体化,我们应该期待从出现的客户端架构中得到两个结果。首先,它应该创造出让人感觉 "下一代 "的网络应用,就像今天构建良好的应用比几年前的应用提供了微妙但明显更好的体验一样。

其次,也许更重要的是,我们改进的客户端架构应该具有超越更好性能的深远影响。通过涉足并解决复杂问题,前端工程师将为系统和思想达成一个更好的模型。一个更好的架构实际上代表了一个更强大的启发式方法。这导致了后续的好处,而这些好处往往是不可预知的。

你可以从反应性本身看到这一点。反应性突然出现在舞台上,因为它提供了一种将状态绑定从开发者的大脑中卸载到框架中的方法。 但好处并不限于此。架构不仅变得更简单,而且更一致。这使得性能和功能全面提升。

由于现代JavaScript框架同时包含了服务器和客户端,这些发展的结果可能会对一般的应用架构产生广泛的影响。

改善水合作用的方法

改善水合情况的基本诀窍是更加细化地看待事物。 通过将视图、交互性和状态分解成更小的部分,我们可以逐步加载和激活它们,为FCP和TTI进行优化。 下面是一些方法的介绍。

完全避免使用JavaScript

一种已经被吸收到最佳实践中的方法是分析网站中那些完全不需要JavaScript的页面。这与较新的多页面应用程序 (MPA)的概念有关。这是一种介于单页应用程序(SPA)和直接的每页导航(默认网络行为)之间的中间地带。这里的想法是找到应用中可以立即作为HTML加资产的部分,从而获得最佳的SEO和加载时间。

例如,在SvelteKit中可以看到无JS的方法。当然,这对那些需要反应式交互的页面没有任何作用。框架仍然必须解决那些作为SPA的页面上的水化问题。

岛屿架构

Astro倡导岛屿架构的理念*。*这个想法是要确定页面的哪些部分是静态的,哪些部分需要反应性。有了这些知识,你就可以通过完全忽略那些从未改变的框架内容来微调页面的加载,然后只在需要时加载其他部分(岛屿)。

在理解这个想法时,注意到它是针对改善SPA的,是很有用的。也就是说,你确定的所有静态内容都能够坐在那里,在不影响性能的情况下完成其工作。你所有的客户端状态和导航都得到了维护。

从好的方面来说,这种方法允许你推迟加载每个岛屿,直到发生一些必要的事情(例如滚动到视图,鼠标点击)。缺点是,在实践中,它经常导致在一个特别不合适的时刻(就在用户正在做什么的时候)发生的加载。

懒惰的加载边界

像React的Suspense组件这样的功能提供了一种方法,它保持了基本的水化模型,但沿着边界分解,然后被懒惰地加载。这样做的好处是保持了大部分熟悉的过程,但缺点是需要开发人员进行大量的思考和调整以达到良好的效果。从心理上讲,开发者处于连接组件布局和构建时代码分割的位置。

此外,懒惰加载只能起到这么大的作用,因为大部分的框架仍然需要在前期进行交付。

可重现性

可重述性是由Qwik框架引入的一个想法。Qwik深入研究了应用程序的元素,并在它们之间建立了懒惰的边界(在某种程度上,你可以把它看作是懒惰加载边界的一种高度复杂的形式)。可恢复性意味着客户端可以从服务器离开的地方继续前进,并以一种细粒度的方式保持事情的同步。

服务器组件

React推出了服务器组件的概念和相关的性能改进,称为流。这里描述了服务器组件的工作原理。 从本质上讲,服务器组件允许你确定应用程序的哪些部分可以完全在服务器上运行,从而避免任何客户端的渲染惩罚。

流媒体

流是另一个与Suspense有关的不断发展的React技术。这里的想法是允许像HTML这样的框架内容在所有需要的数据在服务器上准备好之前就开始运送到客户端。然后,这可以在组件交互发生时应用。

部分水化或渐进水化

这些术语让事情变得有点复杂。Astro将其岛屿架构描述为部分水化。 这是简单地说,每次只有页面的某些元素被水化。这有时也被称为渐进式水化。 这两个术语有时都适用于其他技术。

我们真的有三个术语在这里互相踩着脚趾:岛屿、部分、渐进。不管怎么样,主要的想法是一样的:我们需要将应用程序的结构分解成更小的块,以便使其更智能地加载。

分区的水化?

让我们试着把这些术语分解一下。比方说,岛屿架构指的是静态框架内独立的互动性的Astro风格的大块。

往上看,你可以说分解用户界面的整个想法是部分水合,而Astro的岛屿是它的一个例子。不过,我们不能毫无顾忌地这样做,因为Astro == island == partial已经在外面漂浮了。另外,部分似乎暗示了不完整的水合状态,这是个误导。

然后,渐进式会引起与渐进式网络应用程序(PWA)的混淆。也许分区的水合 是一个很好的术语,适合于总体的想法。

前端架构的演变

围绕着JavaScript的前端架构的活动创造了一些我见过的最有趣的代码工作。这是一个充满激情的空间,他们正在探索新的概念领域,并进行开创性的编程。他们以一种开放和协作的方式互动和分享他们的想法。这是一个令人愉快的观察。