QWIK DOCS / V2

响应式

Qwik 用 signal 与 proxy 记录订阅关系,只更新真正依赖状态的 DOM。

约 2 分钟Qwik v206 / 19
本页目录3 节
  1. 订阅图从哪里来
  2. 更新粒度
  3. 乱序渲染

Qwik 不做脏检查,也不在每次状态变化时重渲染整棵组件树。状态通过 signal 和 store proxy 暴露;读取时建立订阅,写入时只更新订阅者。

01订阅图从哪里来#

组件在服务端第一次渲染时,Qwik 会记录模板读取了哪些状态。比如下面的 store.count,会让计数文本节点订阅 count。这份映射与状态一起被序列化。

tsxsrc/components/counter.tsx
export const Counter = component$(() => {
  const store = useStore({ count: 0 });

  return (
    <button onClick$={() => store.count++}>
      {store.count}
    </button>
  );
});

浏览器点击按钮后,事件处理函数恢复 store,写入 count。proxy 捕获写入,找到订阅了 count 的文本节点,直接更新它。整个组件函数不需要重新运行。

02更新粒度#

当一个组件确实需要重新渲染时,只有属性发生变化的子组件会失效。下面的例子中,点击第三颗按钮不会导致任何 Child 重新渲染,也不会加载它们的代码:

tsx只在 props 变化时更新子组件
export const Child = component$((props: { count: number }) => {
  return <span>{props.count}</span>;
});

export const App = component$(() => {
  const store = useStore({ a: 0, b: 0, c: 0 });

  return (
    <>
      <button onClick$={() => store.a++}>a</button>
      <button onClick$={() => store.b++}>b</button>
      <button onClick$={() => store.c++}>c</button>
      <Child count={store.a} />
      <Child count={store.b} />
    </>
  );
});

03乱序渲染#

Qwik 组件可以脱离父组件独立渲染。父组件不需要先执行,子组件也不需要跟着父组件一起重渲染。这是渐进式加载能够成立的前提:恢复一个组件,不必恢复它的整条祖先链。

这种独立性要求 props 可序列化。父组件创建 props,子组件从 HTML 中恢复它们,而不是重新执行父组件。

store 会自动把嵌套对象也包成 proxy,方便是方便,但对象身份会变化。需要浅层跟踪时,可以传 { deep: false }。

理解了订阅图,就可以进入状态的具体 API。