可恢复与水合
Qwik 应用程序的一个关键概念是它们可以从服务器端渲染的状态恢复。解释可恢复性的最佳方式是了解当前一代框架是如何可重放(水合)的。
当 SSR/SSG 应用程序在客户端启动时,它需要客户端上的框架恢复三部分信息
- 监听器 - 找到事件监听器并将其安装在 DOM 节点上,以使应用程序具有交互性。
- 组件树 - 构建一个表示应用程序组件树的内部数据结构。
- 应用程序状态 - 恢复在服务器上获取或保存在
store
中的任何数据。
总的来说,这被称为水合。所有当前一代框架都需要此步骤才能使应用程序具有交互性。
水合很昂贵,原因有两个
- 框架必须下载与当前页面关联的所有组件代码。
- 框架必须执行与页面上组件关联的模板,以重建监听器位置和内部组件树。
Qwik 与众不同,因为它不需要水合就能在客户端恢复应用程序。不需要水合是 Qwik 应用程序启动即时的原因。
所有其他框架的水合都会重放所有应用程序逻辑在客户端。相反,Qwik 在服务器上暂停执行,并在客户端恢复执行。
介绍可恢复性
可恢复性是指在服务器上暂停执行,并在客户端恢复执行,而无需重放和下载所有应用程序逻辑。
一个好的思维模型是,Qwik 应用程序在其生命周期的任何时刻都可以被序列化并移动到不同的 VM 实例(服务器到浏览器)。在那里,应用程序只需从序列化停止的地方恢复。不需要水合。因此,Qwik 应用程序不会水合;它们会恢复。
为了实现这一点,Qwik 需要以一种与无代码启动兼容的方式解决 3 个问题(监听器、组件树、应用程序状态)。
监听器
没有事件监听器的 DOM 只是一个静态页面;它不是一个应用程序。如今,所有网站的标准都是相当高的交互性,因此即使是最静态的网站也充满了事件监听器。这些包括菜单、悬停、展开细节,甚至完整的交互式应用程序。
现有的框架通过下载组件并执行其模板来解决事件监听器问题,从而收集然后附加到 DOM 的事件监听器。当前方法存在以下问题
- 需要预先下载模板代码。
- 需要预先执行模板代码。
- 需要预先下载事件处理程序代码(以进行附加)。
上述方法无法扩展。随着应用程序变得越来越复杂,需要预先下载和执行的代码量会随着应用程序的大小成比例地增长。这会对应用程序启动性能产生负面影响,从而影响用户体验。在其他框架中,应用程序的延迟加载本身就成为一项开发任务,需要花费时间和精力。在 Qwik 中,延迟加载是可恢复架构的自然结果。
Qwik 通过将事件监听器序列化到 DOM 中来解决上述问题,如下所示
<button on:click="./chunk.js#handler_symbol">click me</button>
Qwik 仍然需要收集监听器信息,但此步骤是在 SSR/SSG 的一部分完成的。然后将 SSR/SSG 的结果序列化为 HTML,以便浏览器无需执行任何操作即可恢复执行。请注意,on:click
属性包含所有信息,以便在不执行任何预先操作的情况下恢复应用程序。
- Qwikloader 设置了一个全局监听器,而不是每个 DOM 元素多个监听器。此步骤可以在没有应用程序代码的情况下完成。
- HTML 包含一个指向代码块的 URL 和符号名称。该属性告诉 Qwikloader 下载哪个代码块,从代码块中检索哪个符号,然后执行该符号。
- 最后,为了使所有这些成为可能,Qwik 的事件处理实现理解异步性,这使它能够自动延迟加载闭包。
组件树
框架使用组件树。为此,框架需要完全了解组件树,才能知道哪些组件需要重新渲染以及何时重新渲染。如果您查看现有的框架 SSR/SSG 输出,组件边界信息已被破坏。通过查看生成的 HTML,无法知道组件边界在哪里。为了重新创建此信息,框架会重新执行组件模板并记忆组件边界位置。重新执行就是水合。水合很昂贵,因为它需要下载组件模板并执行它们。
Qwik 在 SSR/SSG 的一部分收集组件边界信息,然后将该信息序列化为 HTML。结果是 Qwik 可以
- 在没有实际存在组件代码的情况下重建组件层次结构信息。组件代码可以保持延迟。
- Qwik 可以仅针对需要重新渲染的组件延迟执行此操作,而不是所有组件都预先执行。
- Qwik 收集商店和组件之间的关系信息。这创建了一个订阅模型,告知 Qwik 哪些组件需要由于状态更改而重新渲染。订阅信息也会被序列化到 HTML 中。
应用程序状态
现有的框架通常有一种方法可以将应用程序状态序列化到 HTML 中,以便在水合过程中恢复状态。从这个意义上说,它们与 Qwik 非常相似。但是,Qwik 将状态管理更紧密地集成到组件的生命周期中。在实践中,这意味着组件可以独立于组件状态进行延迟加载。这在现有框架中很难实现,因为组件道具通常由父组件创建。这会产生连锁反应。为了恢复组件 X,它的父组件也需要恢复。Qwik 允许任何组件在没有父组件代码的情况下恢复。
序列化
理解序列化最简单的方法是通过 JSON.stringify
。但是,JSON 有几个限制。Qwik 可以克服一些限制,但有些限制无法克服,它们限制了开发人员可以做什么。在构建 Qwik 应用程序时,了解这些限制非常重要。
Qwik 解决的 JSON 限制
- JSON 生成一个 DAG。DAG 代表有向无环图,这意味着正在被序列化的对象不能有循环引用。这是一个很大的限制,因为应用程序状态通常是循环的。Qwik 确保当对象图被序列化时,循环引用会被正确保存并恢复。
- JSON 无法序列化某些对象类型。例如,DOM 引用或日期。Qwik 的序列化格式确保此类对象可以正确序列化和恢复。以下是可以使用 Qwik 序列化的类型列表
- DOM 引用
- Promise(参见 资源)
- 函数闭包(如果包装在 QRL 中)
- 日期
URL
对象Map
和Set
实例。
Qwik 未解决的 JSON 限制
对于无法序列化的情况,代码应该在 客户端运行。
编写考虑可序列化性的应用程序
框架的可恢复性功能必须扩展到应用程序的可恢复性。这意味着框架必须为开发人员提供机制来以可序列化并恢复的方式(无需重新引导)表达应用程序的组件和实体。这需要应用程序在编写时考虑可恢复性约束。开发人员不可能继续以堆为中心的方式编写应用程序,并期望一个更好的框架能够弥补这种次优方法。
开发人员必须以 DOM 为中心的方式编写他们的应用程序。这将需要改变行为并重新调整 Web 开发人员的技能。框架需要提供指导和 API,使开发人员能够轻松地以这种方式编写应用程序。
可恢复性的其他好处
使用可恢复性的最明显好处是用于服务器端渲染。但是,还有一些次要好处
- 序列化现有的 PWA 应用程序,以便用户在返回应用程序时不会丢失上下文
- 改进渲染性能,因为只需要重新渲染更改的组件
- 细粒度的延迟加载
- 减少内存压力,尤其是在移动设备上
- 现有静态网站的渐进式交互性