Vue verwendet das Signals-Paradigma zur Handhabung und Verfolgung von Reaktivität. Ein Hauptmerkmal dieses Systems ist, dass das reaktive System Updates nur auf speziell beobachtete reaktive Eigenschaften auslöst. Eine Folge davon ist, dass Sie auch sicherstellen müssen, dass die Queries aktualisiert werden, wenn sich die Werte, die sie konsumieren, ändern.
Bei der Erstellung eines Composables für eine Query könnte Ihre erste Wahl darin bestehen, es wie folgt zu schreiben
export function useUserProjects(userId: string) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId),
);
}
export function useUserProjects(userId: string) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId),
);
}
Wir könnten dieses Composable wie folgt konsumieren
// Reactive user ID ref.
const userId = ref('1')
// Fetches the user 1's projects.
const { data: projects } = useUserProjects(userId.value)
const onChangeUser = (newUserId: string) => {
// Edits the userId, but the query will not re-fetch.
userId.value = newUserId
}
// Reactive user ID ref.
const userId = ref('1')
// Fetches the user 1's projects.
const { data: projects } = useUserProjects(userId.value)
const onChangeUser = (newUserId: string) => {
// Edits the userId, but the query will not re-fetch.
userId.value = newUserId
}
Dieser Code funktioniert nicht wie beabsichtigt. Das liegt daran, dass wir den Wert direkt aus dem userId-Ref extrahieren. Vue-Query verfolgt nicht das userId Ref, daher weiß es nicht, wann sich der Wert ändert.
Glücklicherweise ist die Behebung dieses Problems trivial. Der Wert muss im Query-Schlüssel verfolgbar gemacht werden. Wir können das Ref direkt im Composable akzeptieren und es im Query-Schlüssel platzieren
export function useUserProjects(userId: Ref<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId.value),
);
}
export function useUserProjects(userId: Ref<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId.value),
);
}
Jetzt wird die Query neu geladen, wenn sich der userId ändert.
const onChangeUser = (newUserId: string) => {
// Query refetches data with new user ID!
userId.value = newUserId
}
const onChangeUser = (newUserId: string) => {
// Query refetches data with new user ID!
userId.value = newUserId
}
In Vue Query werden alle reaktiven Eigenschaften innerhalb eines Query-Schlüssels automatisch auf Änderungen überwacht. Dies ermöglicht es Vue-Query, Daten neu zu laden, wann immer sich die Parameter für eine bestimmte Anfrage ändern.
Obwohl weitaus unwahrscheinlicher, ist die Übergabe nicht-reaktiver Variablen manchmal beabsichtigt. Zum Beispiel müssen einige Entitäten nur einmal abgerufen werden und müssen nicht verfolgt werden, oder wir invalidieren eine Mutation in den Query-Optionen nach einer Mutation. Wenn wir unser oben definiertes benutzerdefiniertes Composable verwenden, fühlt sich die Verwendung in diesem Fall etwas seltsam an
const { data: projects } = useUserProjects(ref('1'))
const { data: projects } = useUserProjects(ref('1'))
Wir müssen ein intermediäres Ref erstellen, nur um den Parameter typkompatibel zu machen. Wir können das besser machen. Lassen Sie uns stattdessen unser Composable aktualisieren, um sowohl einfache Werte als auch reaktive Werte zu akzeptieren
export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
);
}
export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
);
}
Jetzt können wir das Composable sowohl mit einfachen Werten als auch mit Refs verwenden
// Fetches the user 1's projects, userId is not expected to change.
const { data: projects } = useUserProjects('1')
// Fetches the user 1's projects, queries will react to changes on userId.
const userId = ref('1')
// Make some changes to userId...
// Query re-fetches based on any changes to userId.
const { data: projects } = useUserProjects(userId)
// Fetches the user 1's projects, userId is not expected to change.
const { data: projects } = useUserProjects('1')
// Fetches the user 1's projects, queries will react to changes on userId.
const userId = ref('1')
// Make some changes to userId...
// Query re-fetches based on any changes to userId.
const { data: projects } = useUserProjects(userId)
Es ist üblich, neue reaktive Zustände aus einer anderen Quelle reaktiver Zustände abzuleiten. Häufig äußert sich dieses Problem in Situationen, in denen Sie mit Komponenteneigenschaften (Props) arbeiten. Nehmen wir an, unser userId ist eine Prop, die an eine Komponente übergeben wird
<script setup lang="ts">
const props = defineProps<{
userId: string
}>()
</script>
<script setup lang="ts">
const props = defineProps<{
userId: string
}>()
</script>
Sie könnten versucht sein, die Prop direkt in der Query wie folgt zu verwenden
// Won't react to changes in props.userId.
const { data: projects } = useUserProjects(props.userId)
// Won't react to changes in props.userId.
const { data: projects } = useUserProjects(props.userId)
Ähnlich wie im ersten Beispiel ist dies jedoch nicht reaktiv. Der Zugriff auf Eigenschaften bei reaktiven Variablen führt zum Verlust der Reaktivität. Wir können dies beheben, indem wir diesen abgeleiteten Zustand über ein computed reaktiv machen
const userId = computed(() => props.userId)
// Reacts to changes in props.userId.
const { data: projects } = useUserProjects(userId)
const userId = computed(() => props.userId)
// Reacts to changes in props.userId.
const { data: projects } = useUserProjects(userId)
Dies funktioniert wie erwartet. Diese Lösung ist jedoch nicht immer die optimale. Abgesehen von der Einführung einer intermediären Variable erstellen wir auch einen memoisierten Wert, der einigermaßen unnötig ist. Für triviale Fälle einfachen Eigenschaftszugriffs ist computed eine Optimierung ohne wirklichen Nutzen. In diesen Fällen ist eine geeignetere Lösung die Verwendung von reaktiven Gettern. Reaktive Getter sind einfach Funktionen, die einen Wert basierend auf einem reaktiven Zustand zurückgeben, ähnlich wie computed funktioniert. Im Gegensatz zu computed memoieren reaktive Getter ihre Werte nicht, was sie zu einem guten Kandidaten für einfachen Eigenschaftszugriff macht.
Lassen Sie uns unser Composable noch einmal umstrukturieren, aber diesmal werden wir es so gestalten, dass es ein Ref, einen einfachen Wert oder einen reaktiven Getter akzeptiert
export function useUserProjects(userId: MaybeRefOrGetter<string>) {
...
}
export function useUserProjects(userId: MaybeRefOrGetter<string>) {
...
}
Lassen Sie uns unsere Verwendung anpassen und nun einen reaktiven Getter verwenden
// Reacts to changes in props.userId. No `computed` needed!
const { data: projects } = useUserProjects(() => props.userId)
// Reacts to changes in props.userId. No `computed` needed!
const { data: projects } = useUserProjects(() => props.userId)
Dies gibt uns eine knappe Syntax und die benötigte Reaktivität ohne unnötigen Memoierungs-Overhead.
Oben haben wir nur eine Query-Option behandelt, die reaktive Abhängigkeiten verfolgt. Zusätzlich zu queryKey erlaubt aber auch enabled die Verwendung von reaktiven Werten. Dies ist nützlich in Situationen, in denen Sie das Abrufen einer Query basierend auf abgeleitetem Zustand steuern möchten
export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
enabled: () => userId.value === activeUserId.value,
);
}
export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
enabled: () => userId.value === activeUserId.value,
);
}
Weitere Details zu dieser Option finden Sie auf der Referenzseite useQuery.
Ergebnisse von useQuery sind immer unveränderlich (immutable). Dies ist aus Leistungs- und Caching-Gründen erforderlich. Wenn Sie einen von useQuery zurückgegebenen Wert ändern möchten, müssen Sie eine Kopie der Daten erstellen.
Eine Auswirkung dieses Designs ist, dass die Übergabe von Werten von useQuery an eine Zwei-Wege-Bindung wie v-model nicht funktioniert. Sie müssen eine veränderbare Kopie der Daten erstellen, bevor Sie versuchen, sie vor Ort zu aktualisieren.