响应式
Qwik 用 signal 与 proxy 记录订阅关系,只更新真正依赖状态的 DOM。
Qwik 不做脏检查,也不在每次状态变化时重渲染整棵组件树。状态通过 signal 和 store proxy 暴露;读取时建立订阅,写入时只更新订阅者。
01订阅图从哪里来#
组件在服务端第一次渲染时,Qwik 会记录模板读取了哪些状态。比如下面的 store.count,会让计数文本节点订阅 count。这份映射与状态一起被序列化。
export const Counter = component$(() => {
const store = useStore({ count: 0 });
return (
<button onClick$={() => store.count++}>
{store.count}
</button>
);
});浏览器点击按钮后,事件处理函数恢复 store,写入 count。proxy 捕获写入,找到订阅了 count 的文本节点,直接更新它。整个组件函数不需要重新运行。
02更新粒度#
当一个组件确实需要重新渲染时,只有属性发生变化的子组件会失效。下面的例子中,点击第三颗按钮不会导致任何 Child 重新渲染,也不会加载它们的代码:
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。