使用 Qwik 进行可恢复的 JavaScript

2025-06-07

使用 Qwik 进行可恢复的 JavaScript

当 Misko Hevery(AngularJS 的创始人)向你介绍他的新框架时,你肯定会停下来听。我之前就已经了解Qwik,也看到了它的潜力,但这次见面还是让我停下来仔细看看。

Qwik是一个独特的 JavaScript 框架,因为它是目前唯一一个能够在组件级别实现无序运行的框架。Qwik 的突破之处在于,它为 JavaScript 语言引入了一个全新的概念:可恢复框架。


可恢复框架?

图片描述

如今,JavaScript 框架通常都是同构的。也就是说,能够在服务器端和浏览器端渲染。但大多数框架都是事后才添加这种能力的。这是对客户端范式的自然扩展。但如果框架一开始就基于服务器端渲染 (SSR) 构建,情况会怎样呢?

在 Qwik 之前,我们见过的同构框架少得可怜。Meteor、Marko,或许还有其他几个。然而,现代同构架构建立在 React、Vue 和 Svelte 等库的基础之上,而这些库最初并非为服务器渲染而创建。

所以毫不奇怪,他们的核心机制并没有设计成利用这些信息。如果你知道你的应用总是会先在服务器上渲染,你会做出什么样的让步呢?

最强大的功能可能是无需在浏览器中重复执行任何已在服务器上完成的工作。这是一个旨在减少浏览器工作量的 JavaScript 框架。它并非第一个这样做的框架,但或许是第一个实现理想化 Hydration 执行的框架。

Hydration 是为服务器渲染的 HTML 添加交互性的过程。它涉及附加事件处理程序和初始化应用程序状态。对于大多数 JavaScript 库来说,这是一个自上而下的过程,非常类似于在浏览器中重新渲染整个应用程序,即使它实际上不会创建任何新的 DOM 节点。有关 Hydration 的更完整指南,请参阅“为什么 JavaScript 框架中的高效 Hydration 如此具有挑战性”


可恢复性之旅

图片描述

创建一种无需在浏览器中重复工作的 Hydration 方法并非易事。你不可能简单地选择现有的单页应用框架就能实现这一点。过去几年,我们在Marko中一直在研究同样的问题,尽管方法不同,但实际上可以归结为几个关键点。

  1. 能够将水合所需的代码(事件、效果)与呈现视图和管理状态更新所需的代码分开。

  2. 了解哪些数据是有状态的(可以更新)以及哪些数据依赖于它。为了恢复工作,必须在比组件更精细的级别上进行,因为在 hydration 期间重新运行组件是不必要的工作。

  3. 序列化足够的数据,以便不相关的更改不需要重新计算,并且应用程序的各个部分可以独立且无序地进行水合。

有些框架可能只实现其中一种,但几乎没有框架同时实现这三种。Qwik 通过遵循组件编写规则、响应式原语(类似于 React Hooks)以及使用编译器利用 JSX 中的标记来指示代码的拆分方式来实现这一点。

图片描述

请注意$TodoMVC 标题部分中将组件代码与视图和事件处理程序分开的指示器。


那么延迟加载怎么样?

除了可恢复之外,Qwik 最突出的功能之一是其渐进式加载功能。它会根据需要逐步加载 JavaScript。它可以从 0kb 的 JavaScript 组件代码开始加载,并根据页面的交互需求逐步扩展。

Qwik 的做法截然不同。其他试图解决此问题的人则利用服务器与客户端之间的区别来做出选择。这些解决方案依赖于“岛”特殊文件扩展名,甚至高级编译器分析。在我看来,这才是需要解决的 80% 问题。一旦移除异步数据加载和路由考虑,大多数页面几乎都是静态的。但如果页面具有极高的交互性呢?如果页面的大部分内容都可以在浏览器中加载呢?

那么,在这种情况下,渐进式数据同步可能是在初始加载时获得响应式页面的唯一方法。而且,这并非仅仅延迟不可避免的加载过程那么简单。这只会将全部成本推到用户首次与页面交互时。并非如此。Qwik 的有趣之处在于,它那些允许其可恢复的特性,也允许页面的任何部分独立地进行数据同步。

是的。页面中间的按钮可以在加载任何更高层级的 JavaScript 代码之前加载将商品添加到购物车所需的代码。这不是典型的框架的工作方式。如果你的组件包含其他组件并传递 props,那么事情需要自上而下地运行。


那么问题解决了吗?

图片描述

嗯,也许吧。但可能和你想象的不一样。理解了我上面的解释后,我觉得做一个演示来真正展示 Qwik 的这些独特功能应该会很有趣。想象一下:

像您习惯的那样,使用 JSX 和响应式数据编写典型的单页应用 (SPA),除了页面加载时几乎没有任何 JS 加载。当您向下滚动一点,发现感兴趣的内容时,该部分的 JavaScript 代码就会加载并运行。点击链接后,客户端路由器会立即加载,客户端导航将接管一切。无缝的 SPA 体验,完美的按需加载。

直到你意识到,当你导航到那个新页面时,你正在加载整个应用的路由信息​​,并且突然加载了几十个新的迷你 JS 文件来在浏览器中渲染整个页面。一开始你可能会觉得这不太好。但后来你又想,我们可以通过捆绑来做一些更智能的事情。Qwik 正在研究一些智能的捆绑方法。但这远不止于此。

对于一个优化一切以减少浏览器中的 JavaScript 的框架,为什么你甚至想要在浏览器中呈现整个下一页?

嗯,你不会。这时一切才真正有意义。用现有框架的优劣来评估 Qwik 毫无意义。Qwik 似乎是解决 React 包大小问题的灵丹妙药,但实际上它完全是另一回事。


美丽新世界

图片描述

那么 Qwik 是什么?它就是我在本文中提到的所有内容。它是一个经过优化的框架,无论应用程序的组成如何,都能在浏览器中完成最少的初始工作。更重要的是,它揭示了我们在前端构建应用程序的新范式的潜力。它不是简单地从 SPA 过渡,而是完全以充分利用服务器为目标构建的。

它还比较新。很多功能还没有文档记录。还有一些问题需要解决。

经典的服务器多页面应用路由显然能帮助我们在切换到新页面时保持体验。切换到新位置时,服务器渲染允许 Qwik 默认继续不发送 JavaScript。我预计未来该领域将出现更多发展,带来无需重新加载完整页面的服务器渲染页面和部分页面。

渐进式数据融合仍然是一个棘手的问题,因为它确实存在成本。关键交互不应该被延迟加载,而应该以合理的方式集中加载,以防止代码拆分瀑布。Qwik 有一个优化器,可以让你控制内容的打包方式。未来,你将能够将网站分析数据(用户与页面的交互方式)提供给打包流程。我知道这听起来有点疯狂,但这也是这种方法的考量之一。现在,你可以在他们的在线测试区试用这个优化器。

数据加载和序列化仍需考虑。其他一些部分数据融合解决方案利用它们知道哪些数据仅存在于服务器端这一特性,仅对所需数据进行序列化。利用这些数据需要作为 props 传递给顶级浏览器组件这一特性,可以显著减少双重数据问题(既需要将其表示为 JSON,又需要将其渲染为 HTML)。Qwik 本身并不具备这方面的知识,但它的数据融合方法并非限制因素。因此,看看他们采用何种方法将会非常有趣。


结论

我很高兴能用Qwik制作几个演示( Hackernews和 JS Framework Benchmark),我看到了一个非常有前途的框架的雏形。但在目前的环境下,评估它还是有点困难,因为我觉得我们还没有看到它的全貌。这不仅是因为 Qwik 仍在开发中,还因为更广泛的生态系统和工具还没有真正跟上这一转变。但这只是时间问题。

与此同时,Qwik提供了解决 JavaScript 代码过多问题的独特方法之一。它可以轻松处理 100% 的 Lighthouse 评分。如果您正在寻求最大程度地缩短交互时间并尝试新事物,那么 Qwik 无疑是您的最佳选择。


对 Qwik 的工作原理更感兴趣?Misko Hevery 就此主题撰写了一系列精彩的文章:

文章来源:https://dev.to/this-is-learning/resumable-javascript-with-qwik-2i29
PREV
ReactiveScript 的探索
NEXT
可恢复性,WTF?