useQuery()
无需获取即可渲染数据。
访问任意 Queryable Schema 在 store 中的值;例如 Entity、All、Collection、Query、
Union 和 Scalar。Lazy 字段也可以通过其 .query 访问器使用。如果该值不存在,则返回 undefined。
useQuery() 会响应数据变更;只在必要时重新渲染。当数据为 Invalid 时返回 undefined。
用法
Post
PostResource
PostItem.vue
TotalVotes.vue
PostList.vue
<script setup lang="ts"> import { Query } from '@data-client/rest'; import { useQuery } from '@data-client/vue'; import { PostResource } from './PostResource'; const queryTotalVotes = new Query( PostResource.getList.schema, posts => posts.reduce((total, post) => total + post.votes, 0), ); const props = defineProps<{ userId: number }>(); const totalVotes = useQuery(queryTotalVotes, () => ({ userId: props.userId })); </script> <template> <div style="text-align: center"> <small>{{ totalVotes }} votes total</small> </div> </template>
关于类型处理的更多信息,请参阅真值收窄
类型
function useQuery<S extends Queryable>(
schema: S,
...args: MaybeRefsOrGetters<SchemaArgs<S>>
): ComputedRef<DenormalizeNullable<S> | undefined>;
参数可以是普通值、refs(包括 computed),或者像 () => ({ id: props.id }) 这样的 getter
函数。像 { id: props.id } 这样的普通对象只会被读取一次,不会跟随 prop 或路由的变化,因此当参数可能变化时,请使用 getter 或 computed。
参数变化时,结果会随之更新。
Queryable
Queryable schema 需要一个有返回值的 queryKey() 方法。这包括
Entity、All、Collection、Query、
Union 和 Scalar。Lazy 字段通过其 .query 访问器产生一个 Queryable。
interface Queryable {
queryKey(
args: readonly any[],
queryKey: (...args: any) => any,
getEntity: GetEntity,
getIndex: GetIndex,
// Must be non-void
): {};
}
示例
排序与过滤
Query 提供了以编程方式访问 Reactive Data Client store 的能力。
UserResource
UsersPage.vue
<script setup lang="ts"> import { Query, All } from '@data-client/rest'; import { useQuery, useFetch } from '@data-client/vue'; import { UserResource, User } from './UserResource'; interface Args { asc: boolean; isAdmin?: boolean; } const sortedUsers = new Query( new All(User), (entries, { asc, isAdmin }: Args = { asc: false }) => { let sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name), ); if (isAdmin !== undefined) sorted = sorted.filter(user => user.isAdmin === isAdmin); if (asc) return sorted; return sorted.reverse(); }, ); useFetch(UserResource.getList); const users = useQuery(sortedUsers, { asc: true }); </script> <template> <div v-if="!users">No users in cache yet</div> <div v-else> <div v-for="user in users" :key="user.pk()">{{ user.name }}</div> </div> </template>
Lazy 关系
Lazy 字段在父级反规范化时保留原始 ID。将 .query 与 useQuery 搭配使用即可按需解析它们,从而把重新渲染限制在真正需要相关数据的组件内。
Resources
BuildingList.vue
DepartmentsPage.vue
<script setup lang="ts"> import { computed } from 'vue'; import { useQuery } from '@data-client/vue'; import { Department } from './Resources'; const props = defineProps<{ dept: Department }>(); const buildings = useQuery( Department.schema.buildings.query, computed(() => props.dept.buildings), ); </script> <template> <span v-if="buildings">{{ buildings.map(b => b.name).join(', ') }}</span> </template>
<script setup lang="ts"> import { All } from '@data-client/rest'; import { useQuery, useFetch } from '@data-client/vue'; import { DepartmentResource, Department } from './Resources'; import BuildingList from './BuildingList.vue'; useFetch(DepartmentResource.getList); const departments = useQuery(new All(Department)); </script> <template> <div v-if="!departments">Loading...</div> <div v-else> <div v-for="dept in departments" :key="dept.pk()"> <strong>{{ dept.name }}</strong>: <BuildingList :dept="dept" /> </div> </div> </template>