QWIK DOCS / V2

可恢复性

Qwik 不做水合,而是把服务端状态序列化进 HTML,在浏览器从断点继续。

约 3 分钟Qwik v204 / 19
本页目录5 节
  1. 监听器:写进 DOM
  2. 组件树:边界信息随 HTML 一起到达
  3. 状态:不只是 JSON
  4. 为什么这能加速启动
  5. 写应用时的约束

可恢复性(resumability)的核心是一句话:在服务端暂停执行,在浏览器从暂停处继续。应用不需要在客户端重放服务端做过的事。

要理解它,先看水合必须恢复的三件东西:监听器、组件树、应用状态。Qwik 用序列化逐个解决,因此浏览器可以在没有任何应用代码的情况下启动。

01监听器:写进 DOM#

常规框架要下载组件、执行模板,才能知道哪个 DOM 节点该绑定哪个函数。Qwik 在 SSR 阶段就把监听器信息写进属性,浏览器只需要一个全局监听器来代理事件:

htmlSSR 输出(示意)
<button q-e:click="./chunk.js#s_LToOrb0bX0M">
  click me
</button>

Qwikloader 先在 document 上注册一个通用监听器。用户点击时,它从属性里读出 chunk 与符号名,按需下载并执行。事件处理函数不是页面启动的一部分,而是交互的结果。

02组件树:边界信息随 HTML 一起到达#

水合需要知道组件边界,才能决定状态变化时重渲染谁。常规 HTML 丢失了这些信息,所以框架必须重新执行模板。Qwik 把组件层次、属性与 DOM 的对应关系序列化进 HTML,浏览器不需要执行组件就能恢复结构。

更重要的是,Qwik 把状态与 DOM 的订阅关系也写进了 HTML。当某个 signal 改变时,Qwik 知道需要更新哪几个文本节点或属性,而不是重跑整个组件。

03状态:不只是 JSON#

JSON 无法表达循环引用、Date、Map、Set、Promise、DOM 引用和函数闭包。Qwik 的序列化格式支持这些类型,因此 store、资源与事件闭包可以跨过服务端与浏览器的边界。

可以序列化不能序列化
基本类型、对象、数组自定义 class 的实例与原型
Date、URL、Map、SetStreams
Promise、资源未包装为 QRL 的函数
QRL 包装的闭包DOM 节点之外的宿主对象
tsx闭包的捕获规则
component$(() => {
  const label = '可序列化'; // const + 可序列化
  return <button onClick$={() => console.log(label)}>log</button>;
});

04为什么这能加速启动#

  • 浏览器不需要下载组件模板就能让页面响应。
  • 每个组件、任务、事件都可以独立恢复,父组件代码不必存在。
  • 状态变化只更新订阅它的 DOM,减少内存压力与重渲染。
  • 同一份序列化状态可以被缓存、传输,甚至用于离线恢复。

05写应用时的约束#

可恢复性不只属于框架,也属于应用。凡是需要跨过服务端与客户端边界的状态,都应该以可序列化的形式表达。无法序列化的对象可以留在客户端,用 noSerialize() 标记,或者放进只在浏览器运行的 useVisibleTask$。

下一页渐进式加载会解释:既然一切都是符号,Qwik 如何决定什么时候加载哪个符号。