跳到主要内容

useController()

Controller 提供类型安全的方法,用于访问 store 并向其分发 action。

例如 fetch、invalidate 和 setResponse

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

const props = defineProps<{ id: string }>();
const ctrl = useController();

const handleRefresh = async () => {
await ctrl.fetch(MyResource.get, { id: props.id });
};

const handleSuspend = async () => {
await ctrl.invalidate(MyResource.get, { id: props.id });
};

const handleLogout = () => {
ctrl.resetEntireStore();
};
</script>

useController() 必须在 <script setup>(或 setup())中调用,并且需要安装 DataClientPlugin。在模板和选项式 API 中,同一个 Controller 也可以通过 $dataClient 访问。

示例​

表单提交​

fetch 返回反规范化后的响应,与 useSuspense() 的返回类型一致。这样就可以使用 pk() 等 Entity 方法。

CreatePost.vue
<script setup lang="ts">
import { useController } from '@data-client/vue';
import { useRouter } from 'vue-router';
import { PostResource } from './PostResource';

const ctrl = useController();
const router = useRouter();

const handleSubmit = async (e: Event) => {
e.preventDefault();
const post = await ctrl.fetch(
PostResource.getList.push,
new FormData(e.target as HTMLFormElement),
);
post.title;
post.computedField;
router.push(`/post/${post.pk()}`);
};
</script>

<template>
<form @submit="handleSubmit"><!-- fields --></form>
</template>

直接更新 Entity​

使用 set 可以在不发起网络请求的情况下立即更新。它支持函数式更新,以避免竞态条件。

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

const props = defineProps<{ articleId: string }>();
const ctrl = useController();

const vote = () =>
ctrl.set(Article, { id: props.articleId }, article => ({
...article,
votes: article.votes + 1,
}));
</script>

<template>
<button @click="vote">Vote</button>
</template>

变更后使数据失效​

使用 invalidate 或 expireAll 强制重新获取相关数据。

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

const ctrl = useController();

const handleClear = async () => {
// invalidate() causes suspense; expireAll() refetches silently
ctrl.expireAll(UserResource.get);
ctrl.expireAll(UserResource.getList);
};
</script>

<template>
<button @click="handleClear">Refresh user data</button>
</template>
提示

为了获得更好的性能和一致性,优先在变更响应中包含副作用更新。

预取​

使用 fetchIfStale 进行预取,而不会重复获取仍然新鲜的数据。

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

const props = defineProps<{ id: string }>();
const ctrl = useController();

const prefetch = () =>
ctrl.fetchIfStale(ArticleResource.get, { id: props.id });
</script>

<template>
<RouterLink :to="`/article/${id}`" @mouseenter="prefetch">
Read more
</RouterLink>
</template>

Websocket 更新​

通过 set 用外部数据填充缓存。

useWebsocket.ts
import { onMounted, onUnmounted } from 'vue';
import { useController } from '@data-client/vue';
import { EntityMap } from './resources';

export function useWebsocket(url: string) {
const ctrl = useController();
let ws: WebSocket;

onMounted(() => {
ws = new WebSocket(url);
ws.onmessage = event => {
const { entity, args, data } = JSON.parse(event.data);
ctrl.set(EntityMap[entity], args, data);
};
});
onUnmounted(() => ws?.close());
}
注意

在生产环境中,请实现一个用于数据流的 Manager,而不是使用组件级的 生命周期钩子。Manager 会在全局处理连接的生命周期,并且支持 SSR。

Todo 应用​

探索 vue-todo-app 示例

更多演示