跳到主要内容

useDLE() - [D]ata [L]oading [E]rror

高性能的异步数据渲染,不会过度获取,并附带获取的元数据。

如果你无法使用 suspense,useDLE() 与 useSuspense() 完全一样,只是它会返回 [D]ata [L]oading [E]rror(数据、加载、错误)这几个值。

useDLE() 会响应数据变更,只在必要时重新渲染。

用法​

<script setup lang="ts">
  import { useDLE } from '@data-client/vue';
  import { ProfileResource } from './ProfileResource';

  const { data, loading, error } = useDLE(ProfileResource.getList);
</script>

<template>
  <div v-if="error">Error {{ error.status }}</div>
  <Loading v-else-if="loading || !data" />
  <div v-else>
    <div class="listItem" v-for="profile in data" :key="profile.pk()">
      <Avatar :src="profile.avatar" />
      <div>
        <h4>{{ profile.fullName }}</h4>
        <p>{{ profile.bio }}</p>
      </div>
    </div>
  </div>
</template>

行为​

data、loading 和 error 各自都是一个 ComputedRef。请在 <script setup> 的顶层解构它们,这样它们在模板中会被自动解包。下表描述的是它们的 .value。

过期状态获取DataLoadingError条件
Invalid是1undefinedtruefalse不在 store 中、删除、失效、invalidIfStale
Stale是1反规范化后的值falsefalse(首次渲染、参数变化)且 expiry < now
Valid否反规范化后的值false可能2获取完成
否undefinedfalsefalse第二个参数传入 null
备注
  1. 相同的获取会被自动去重
  2. 硬错误会被 onErrorCaptured() 捕获
条件依赖

将 null 作为任意 Data Client hook 的第二个参数,表示“什么都不做”。

// todo could be undefined if id is undefined
const todo = useDLE(
TodoResource.get,
computed(() => (id.value ? { id: id.value } : null)),
);

类型​

function useDLE(
endpoint: ReadEndpoint,
...args: MaybeRefsOrGetters<Parameters<typeof endpoint>> | [null]
): {
data: ComputedRef<DenormalizeNullable<typeof endpoint.schema>>;
loading: ComputedRef<boolean>;
error: ComputedRef<ErrorTypes | undefined>;
};

参数可以是普通值、refs(包括 computed),或者像 () => ({ id: props.id }) 这样的 getter 函数。像 { id: props.id } 这样的普通对象只会被读取一次,不会跟随 prop 或路由的变化,因此当参数可能变化时,请使用 getter 或 computed。

参数变化时,结果会随之更新。

示例​

详情​

<script setup lang="ts">
  import { useDLE } from '@data-client/vue';
  import { ProfileResource } from './ProfileResource';

  const {
    data: profile,
    loading,
    error,
  } = useDLE(ProfileResource.get, { id: 1 });
</script>

<template>
  <div v-if="error">Error {{ error.status }}</div>
  <Loading v-else-if="loading || !profile" />
  <div v-else class="listItem">
    <Avatar :src="profile.avatar" />
    <div>
      <h4>{{ profile.fullName }}</h4>
      <p>{{ profile.bio }}</p>
    </div>
  </div>
</template>

条件获取​

传入 null 可以避免绑定和获取数据

▶Resources
▶PostWithAuthor.vue
<script setup lang="ts">
  import { computed } from 'vue';
  import { useDLE } from '@data-client/vue';
  import { PostResource, UserResource } from './Resources';

  const props = defineProps<{ id: string }>();
  const {
    data: post,
    loading: postLoading,
    error: postError,
  } = useDLE(PostResource.get, () => ({ id: props.id }));
  const {
    data: author,
    loading: authorLoading,
    error: authorError,
  } = useDLE(
    UserResource.get,
    computed(() =>
      post.value?.userId
        ? {
            id: post.value.userId,
          }
        : null,
    ),
  );
</script>

<template>
  <div v-if="postError">Error {{ postError.status }}</div>
  <Loading v-else-if="postLoading || !post" />
  <div v-else-if="authorError">Error {{ authorError.status }}</div>
  <Loading v-else-if="authorLoading || !author" />
  <div v-else>{{ author.username }}</div>
</template>

嵌入数据​

当 Entity 存储在嵌套结构中时,该结构会被保留。

▶api/Post
import { Entity, RestEndpoint, Collection } from '@data-client/rest';

export class PaginatedPost extends Entity {
  id = '';
  title = '';
  content = '';

  static key = 'PaginatedPost';
}

export const getPosts = new RestEndpoint({
  path: '/post',
  searchParams: { page: '' },
  schema: {
    results: new Collection([PaginatedPost]),
    nextPage: '',
    lastPage: '',
  },
});
▶ArticleList.vue
<script setup lang="ts">
  import { useDLE } from '@data-client/vue';
  import { getPosts } from './api/Post';

  const props = defineProps<{ page: string }>();
  const { data, loading, error } = useDLE(getPosts, () => ({ page: props.page }));
</script>

<template>
  <div v-if="error">Error {{ error.status }}</div>
  <Loading v-else-if="loading || !data" />
  <div v-else>
    <div v-for="post in data.results" :key="post.pk()">
      {{ post.title }}
    </div>
  </div>
</template>