QWIK DOCS / V2

状态

useSignal、useStore、useComputed$ 与可序列化状态。

约 3 分钟Qwik v207 / 19
本页目录6 节
  1. useSignal()
  2. useStore()
  3. useComputed$()
  4. useConstant()
  5. 共享状态
  6. 序列化边界

Qwik 的状态分两类:普通可序列化数据,以及能触发 UI 更新的响应式状态。响应式状态用 useSignal() 或 useStore() 创建。

01useSignal()#

signal 是一个带 .value 属性的对象。读写 .value 会被 Qwik 跟踪:

tsxsrc/components/counter.tsx
import { component$, useSignal } from '@qwik.dev/core';

export default component$(() => {
  const count = useSignal(0);

  return (
    <button onClick$={() => count.value++}>
      点击 {count.value} 次
    </button>
  );
});
LIVE / USE_SIGNALidle
00

这个数字由 signal 驱动。点击任意按钮,只有这块 DOM 更新。

把 signal 传给子组件时有两种选择:传 signal.value 只传当前值;传 signal 本身,子组件就能读写它。

02useStore()#

store 是对象版的 signal。Qwik 默认深度跟踪嵌套对象与数组:

tsxcode
const state = useStore({
  name: 'Qwik',
  meta: { stars: 0 },
  tags: ['resumable'],
});

state.meta.stars++;
state.tags.push('streaming');

store 中的方法需要用 $() 包装才能序列化。需要 this 时使用 function 关键字,并把接口单独声明出来——直接在类型参数里写 typeof counter 会触发 TS7022 自引用错误。

tsxcode
import { $, type QRL } from '@qwik.dev/core';

interface CounterStore {
  count: number;
  increment: QRL<(this: CounterStore) => void>;
}

const counter = useStore<CounterStore>({
  count: 0,
  increment: $(function () {
    this.count++;
  }),
});

03useComputed$()#

派生值用 useComputed$。它只重新计算依赖变化的部分,不会让整个组件重新渲染:

tsxcode
const price = useSignal(120);
const quantity = useSignal(2);
const total = useComputed$(() => price.value * quantity.value);

return <p>合计 {total.value} 元</p>;

异步派生值同样可以交给 useComputed$。它不会阻塞首次渲染,并且可以拿到 abortSignal 取消上一次请求:

tsxcode
const query = useSignal('');
const results = useComputed$(async ({ abortSignal }) => {
  const res = await fetch(`/api/search?q=${query.value}`, {
    signal: abortSignal,
  });
  return res.json();
}, { initial: [] });

04useConstant()#

只在组件整个生命周期内计算一次的值,用 useConstant()。它不具响应性,适合随机 id、配置对象这类不需要触发更新的数据。

05共享状态#

  • props:父子组件之间最直接的方式,props 必须可序列化。
  • context:跨越多层组件,见上下文。
  • store 方法:把行为与状态放在同一个可序列化对象里。

06序列化边界#

需要跨过服务端与客户端边界的状态必须可序列化。无法序列化的对象(例如浏览器 API 实例、第三方类实例)可以用 noSerialize() 标记:

tsxcode
import { noSerialize, useStore } from '@qwik.dev/core';

const state = useStore({
  chart: noSerialize(new Chart(canvas)),
});