State
状态管理对任何应用来说都是很重要的一部分内容。 在Qwik里,我们区分了两种类型的状态,reactive和static。
Static状态是任何可以被序列化的东西:a string, number, object, array... 等。
而Reactive状态指的是由useStore()
创建的状态。
有一个很重要的点需要知道,Qwik里的状态不是 组件的状态,而是可以被任何组件实例化的 应用的状态。 It's important to notice that state in Qwik is not component state, but rather app state that can be instantiated by any component.
useStore()
const reactive = useStore(initialState)
是一个创建响应式对象的hook。
它需要一个初始化状态作为参数,然后返回一个响应式对象。
useStore()
创建的响应式对象和任何其他对象一样,只不过是响应式的。
如果你修改这个对象的一个属性,任何依赖这个属性的组件都会更新。
NOTE Make sure to keep a reference to the reactive object and not only to its properties, for reactivity to work. e.g. doing
let { count } = useStore({ count: 0 })
and then mutatingcount
won't trigger updates of components that depends on it.
例子
一个展示useStore
怎么用的例子。
是一个计数器的例子,跟踪count变量。
export const App = component$(() => {
const state = useStore({ count: 0 });
return (
<>
<button onClick$={() => state.count++}>Increment</button>
Count: {state.count}
</>
);
});
上面的例子,App组件使用useStore
创建了一个响应式对象。
这个对象用于跟踪count属性,count属性在组件里显示。
如果点击了按钮,count属性值就会改变,因为组件里访问了count属性,因此组件会更新。
嵌套的值
默认下,useStore()
只跟踪store的顶层字段。
这意味着你想要组件有更新,就必须更新顶层字段的值。
比如下面的例子,组件就不会更新,点了按钮,UI是没反应的:
import { component$, useStore } from '@builder.io/qwik';
export const App = component$(() => {
const store = useStore({
nested: { fields: { are: "not tracked" }}
})
return (
<>
<p>{store.nested.fields.are}</p>
<button onClick$={() => store.nested.fields.are = "tracked"}>Click me</button>
</>
);
});
为了使组件可以更新,我们就必须更新顶层的nested
字段:
store.nested = { fields: { are: { "tracked" } } }
还有第二种方法,为了使上述例子可以工作,我们可以为useStore传递第二个参数{ recursive: true }
,
告诉它要递归跟踪store里的所有字段,无论嵌套多深。
export const App = component$(() => {
const store = useStore({
nested: { fields: { are: "not tracked" }}
}, { recursive: true })
return (
<>
<p>{store.nested.fields.are}</p>
<button onClick$={() => store.nested.fields.are = "tracked"}>Click me</button>
</>
);
});
现在组件将如期更新。
并且{ recursive: true }
这个设置也会跟踪数组的单个数组项。
import { component$, useStore } from '@builder.io/qwik';
export const App = component$(() => {
const store = useStore({
letters: ["A", "B", "C"]
}, { recursive: true })
return (
<>
{store.letters.map(letter => <p>{letter}</p>)}
<button onClick$={() => { store.letters[2] = "Z"}}>Click me</button>
</>
);
});
传递store到其他组件
Qwik一个很好的功能是可以传递state到其他组件。并且双方都可以对state进行读和写,组件树里的数据流动是双向的。
有三种方式(中文翻译没错,下面只介绍了两种,但是原文档写的是三种方式)传递state到其他组件。
用 props
最朴素的方式是将state作为props给其他组件。 这种方式react也支持,Qwik也支持。
export const Parent = component$(() => {
const userData = useStore({
count: 0,
});
return (
<>
<Child userData={userData} />
</>
);
});
export const Child = component$(({ userData }) => {
return (
<>
<button onClick$={() => userData.count++}>Increment</button>
Count: {userData.count}
</>
);
});
用 context API
context API是一种不需要通过props就能把state传递到其他组件的方式。 所有后继组件(子、孙、重孙)都能访问到这个state,并且都能对它进行读和写操作。
更多信息请参考context API。
const CTX = createContext('stuff');
export const Stores = component$(() => {
const userData = useStore({
count: 0,
});
useContextProvider(CTX, userData);
return (
<>
<Child />
</>
);
});
export const Child = component$(() => {
const userData = useContext(CTX);
return (
<>
<button onClick$={() => userData.count++}>Increment</button>
Count: {userData.count}
</>
);
});
计算属性
计算属性是从其他值派生出来的值。 计算属性是很有用的。
Qwik有两种方式创建计算属性,使用useWatch$()
或者 useResource$()
。
这两个的只要区别是useWatch$()
允许副作用而且执行过程是串行的。
而useResource$()
是异步的,多个useResource调用是可以并行的。
useWatch$()
通常用于计算中间state,而useResource$()
更擅长计算最终state,用于渲染。
让我们看看下面的例子:
useWatch$()
例子
export const App = component$(() => {
const state = useStore({
count: 0,
doubleCount: 0
});
useWatch$(({ track }) => {
track(() => state.count);
state.doubleCount = state.count * 2;
});
return (
<>
<button onClick$={() => state.count++}>Increment</button>
Count: {state.count}
Count * 2: {state.doubleCount}
</>
);
});
useResource$()
例子
export const App = component$(() => {
const state = useStore({
count: 0,
});
const doubleCount = useResource$(({ track }) => {
track(() => state.count);
return state.count * 2;
});
return (
<>
<button onClick$={() => state.count++}>Increment</button>
Count: {state.count}
Count * 2: {doubleCount.promise}
</>
);
});
useWatch 和 useResource 都有专门的文档来解释。
Reactivity
由于Qwik的细粒度响应性,只有依赖state的组件才会更新。这是巨大的性能提升,因为只有需要更新的组件才会更新。