queryClient.getQueryData 现在仅接受 queryKey 作为参数queryClient.getQueryState 现在仅接受 queryKey 作为参数refetchInterval 回调函数仅传递 queryremove 方法已从 useQuery 中删除isDataEqual 选项已从 useQuery 中删除cacheTime 重命名为 gcTimeuseErrorBoundary 选项已重命名为 throwOnErrorError 现在是错误的默认类型,而不是 unknownprefer-query-object-syntax 规则已删除keepPreviousData,取而代之的是 placeholderData 标识函数focus 事件navigator.onLine 属性context 属性,取而代之的是自定义 queryClient 实例refetchPage,取而代之的是 maxPagesdehydrate APIinitialPageParamgetNextPageParam 或 getPreviousPageParam 返回 null 现在表示没有更多可用页面status: loading 已更改为 status: pending,isLoading 已更改为 isPending,isInitialLoading 现已重命名为 isLoadinghashQueryKey 已重命名为 hashKeyuseQueries 组合式函数返回 ref 而不是 reactiveuseQueries 的新 combine 选项细粒度存储持久化程序injectionContext 中运行 vue-query 组合式函数v5 是一个主要版本,因此需要注意一些重大更改:
useQuery 及其相关函数以前在 TypeScript 中有许多重载:可以调用该函数的不同方式。这不仅在类型方面难以维护,而且还需要在运行时检查第一个和第二个参数的类型,以正确创建选项。
现在我们只支持对象格式。
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
queryClient.getQueryData 参数已更改为仅接受 queryKey
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
queryClient.getQueryState 参数已更改为仅接受 queryKey
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
为了简化删除重载的迁移,v5 附带了一个代码转换工具。
代码转换工具会尽力帮助您迁移重大更改。请仔细检查生成的代码!此外,代码转换工具无法找到某些边缘情况,因此请密切关注日志输出。
如果要针对 .js 或 .jsx 文件运行它,请使用以下命令:
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
如果要针对 .ts 或 .tsx 文件运行它,请使用以下命令:
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
请注意,在 TypeScript 的情况下,您需要使用 tsx 作为解析器;否则,代码转换工具将无法正确应用!
**注意:**应用代码转换工具可能会破坏您的代码格式,因此请不要忘记在应用代码转换工具后运行 prettier 和/或 eslint!
关于代码转换工具如何工作的一些说明:
onSuccess、onError 和 onSettled 已从查询中删除。它们未对变更进行修改。有关此更改背后的动机以及应采取的措施,请参阅此 RFC。
这简化了回调的调用方式(refetchOnWindowFocus、refetchOnMount 和 refetchOnReconnect 回调也都只传递查询),并且修复了一些当回调获取由 select 转换的数据时的类型问题。
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
您仍然可以使用 query.state.data 访问数据,但是,它不会是经过 select 转换的数据。如果您需要访问转换后的数据,可以在 query.state.data 上再次调用转换。
以前,remove 方法用于从 queryCache 中删除查询,而不会通知观察者。它最适合用于命令式地删除不再需要的数据,例如,在注销用户时。
但是,当查询仍处于活动状态时执行此操作没有多大意义,因为它只会在下一次重新渲染时触发硬加载状态。
如果您仍然需要删除查询,可以使用 queryClient.removeQueries({queryKey: key})
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
主要是因为围绕类型推断发布了一个重要的修复程序。有关更多信息,请参阅此 TypeScript 问题。
以前,此函数用于指示是将先前 data (true) 还是新数据 (false) 用作查询的已解析数据。
您可以通过向 structuralSharing 传递一个函数来实现相同的功能:
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
自定义记录器在版本 4 中已被弃用,并在此版本中被删除。日志记录仅在开发模式下有效,在这种模式下不需要传递自定义记录器。
我们更新了我们的 browserslist 以生成更现代、性能更好且更小的捆绑包。您可以在此处阅读有关要求的信息。
TanStack Query 始终在类上具有私有字段和方法,但它们并非真正的私有——它们只是在 TypeScript 中是私有的。我们现在使用 ECMAScript 私有类特性,这意味着这些字段现在是真正的私有字段,并且无法在运行时从外部访问。
几乎每个人都弄错了 cacheTime。它听起来像是“数据缓存的时间量”,但这是不正确的。
只要查询仍在使用中,cacheTime 就不会执行任何操作。它仅在查询变为未使用状态后才会生效。时间过后,数据将被“垃圾回收”以避免缓存增长。
gc 指的是“垃圾回收”时间。它有点技术性,但在计算机科学中也是一个相当众所周知的缩写。
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
为了使 useErrorBoundary 选项更具框架无关性,并避免与已建立的 React 函数前缀“use”(用于钩子)和“ErrorBoundary”组件名称混淆,它已重命名为 throwOnError 以更准确地反映其功能。
尽管在 JavaScript 中,您可以 throw 任何东西(这使得 unknown 成为最正确的类型),但几乎总是抛出 Errors(或 Error 的子类)。此更改使得在大多数情况下更容易在 TypeScript 中使用 error 字段。
如果您想抛出非 Error 的东西,现在必须自己设置泛型:
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
有关如何全局设置不同类型错误的方法,请参阅 TypeScript 指南。
由于现在唯一支持的语法是对象语法,因此不再需要此规则
我们删除了 keepPreviousData 选项和 isPreviousData 标志,因为它们的功能与 placeholderData 和 isPlaceholderData 标志基本相同。
为了实现与 keepPreviousData 相同的功能,我们将先前的查询 data 作为参数添加到了 placeholderData 中,该参数接受一个标识函数。因此,您只需要向 placeholderData 提供一个标识函数,或者使用 Tanstack Query 中包含的 keepPreviousData 函数。
此处需要注意的一点是,useQueries 不会在 placeholderData 函数中接收 previousData 作为参数。这是由于数组中传递的查询的动态特性,这可能导致占位符和 queryFn 的结果形状不同。
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
在 Tanstack Query 的上下文中,标识函数是指始终返回其提供的参数(即数据)不变的函数。
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // 与 `keepPreviousData` 行为相同的标识函数
})
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // 与 `keepPreviousData` 行为相同的标识函数
})
但是,此更改存在一些需要注意的警告:
placeholderData 始终会将您置于 success 状态,而 keepPreviousData 会为您提供先前查询的状态。如果数据已成功获取然后出现后台重新获取错误,则该状态可能为 error。但是,错误本身并未共享,因此我们决定坚持使用 placeholderData 的行为。
keepPreviousData 为您提供了先前数据的 dataUpdatedAt 时间戳,而使用 placeholderData 时,dataUpdatedAt 将保持为 0。如果您想在屏幕上连续显示该时间戳,这可能会很烦人。但是,您可以使用 useEffect 来解决此问题。
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
现在仅使用 visibilitychange 事件。这是可能的,因为我们仅支持支持 visibilitychange 事件的浏览器。这修复了此处列出的许多问题。
navigator.onLine 在基于 Chromium 的浏览器中效果不佳。存在许多问题围绕假阴性,这导致查询被错误地标记为 offline。
为了规避这个问题,我们现在总是以 online: true 开始,并且只侦听 online 和 offline 事件来更新状态。
这应该会降低假阴性的可能性,但是,对于通过 serviceWorker 加载的离线应用程序(即使没有互联网连接也可���工作),这可能意味着假阳性。
在 v4 中,我们引入了将自定义 context 传递给所有 react-query 钩子的可能性。这允许在使用 MicroFrontends 时进行适当的隔离。
但是,context 是一个仅限 react 的功能。context 所做的只是让我们能够访问 queryClient。我们可以通过允许直接传入自定义 queryClient 来实现相同的隔离。 这反过来将使其他框架能够以与框架无关的方式拥有相同的功能。
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
在 v4 中,我们引入了使用 refetchPage 函数为无限查询定义要重新获取的页面的可能��。
但是,重新获取所有页面可能会导致 UI 不一致。此外,此选项在例如 queryClient.refetchQueries 上可用,但它仅对无限查询有效,对“普通”查询无效。
v5 包含一个新的 maxPages 选项,用于无限查询,以限制存储在查询数据中并重新获取的页面数量。此新功能处理了最初为 refetchPage 页面功能确定的用例,而没有相关问题。
您可以传递给 dehydrate 的选项已简化。查询和变更始终根据默认函数实现进行脱水。要更改此行为,请不要使用已删除的布尔选项 dehydrateMutations 和 dehydrateQueries,而是实现等效的函数 shouldDehydrateQuery 或 shouldDehydrateMutation。要获得完全不水合查询/变更的旧行为,请传入 () => false。
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
以前,我们将 undefined 作为 pageParam 传递给 queryFn,您可以在 queryFn 函数签名中为 pageParam 参数分配默认值。这样做的缺点是在 queryCache 中存储了 undefined,这是不可序列化的。
相反,您现在必须将显式的 initialPageParam 传递给无限查询选项。这将用作第一页的 pageParam:
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
以前,我们允许通过将 pageParam 值直接传递给 fetchNextPage 或 fetchPreviousPage 来覆盖从 getNextPageParam 或 getPreviousPageParam 返回的 pageParams。此功能根本无法与重新获取一起使用,并且未被广泛了解或使用。这也意味着 getNextPageParam 现在是无限查询所必需的。
在 v4 中,您需要显式返回 undefined 以指示没有更多可用页面。我们已将此检查范围扩大到包括 null。
在服务器上,retry 现在默认为 0 而不是 3。对于预取,我们始终默认为 0 次重试,但由于启用了 suspense 的查询现在也可以直接在服务器上执行(从 React18 开始),因此我们必须确保根本不在服务器上重试。
loading 状态已重命名为 pending,类似地,派生的 isLoading 标志已重命名为 isPending。
对于变更,status 也从 loading 更改为 pending,isLoading 标志已更改为 isPending。
最后,向查询中添加了一个新的派生 isLoading 标志,其实现为 isPending && isFetching。这意味着 isLoading 和 isInitialLoading 是相同的东西,但 isInitialLoading 现在已被弃用,并将在下一个主要版本中删除。
要了解此更改背后的原因,请查看 v5 路线图讨论。
因为它还会对变更键进行哈希处理,并且可以在 useIsMutating 和 useMutationState 的 predicate 函数内部使用,这些函数会传递变更。
为了修复与 Vue 2 的兼容性,useQueries 组合式函数现在返回包装在 ref 中的 queries 数组。 以前返回的是 reactive,这导致了多个问题:
通过此更改,所有这些问题都得到了解决。
此外,这使 useQueries 与其他返回所有值作为 refs 的组合式函数保持一致。
为了能够提供遵循 Vue 版本的新功能,我们现在要求 Vue 3 至少为 v3.3 版本。 对 Vue 2.x 的要求保持不变。
v5 还带来了新功能:
我们有一种新的、简化的方式来执行乐观更新,方法是利用从 useMutation 返回的 variables:
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
在这里,我们仅在变更运行时更改 UI 的外观,而不是直接将数据写入缓存。如果只有一个地方需要显示乐观更新,则此方法效果最佳。有关更多详细信息,请查看乐观更新文档。
无限查询在需要无限滚动或分页时非常有用。 但是,获取的页面越多,消耗的内存就越多,这也会减慢查询重新获取过程,因为所有页面都是按顺序重新获取的。
版本 5 为无限查询提供了一个新的 maxPages 选项,允许开发人员限制存储在查询数据中并随后重新获取的页面数量。 您可以根据要提供的 UX 和重新获取性能调整 maxPages 值。
请注意,无限列表必须是双向的,这要求同时定义 getNextPageParam 和 getPreviousPageParam。
无限查询可以像常规查询一样进行预取。默认情况下,仅预取查询的第一页,并将其存储在给定的 QueryKey 下。如果要预取多个页面,可以使用 pages 选项。有关更多信息,请阅读预取指南。
有关更多详细信息,请参阅 useQueries 文档。
有关更多详细信息,请参阅 experimental_createPersister 文档。
以前,vue-query 组合式函数只能在组件的 setup 函数中运行。 我们有一个应急方案,允许在用户将 queryClient 作为组合式函数选项提供的情况下在任何地方运行这些钩子。
现在,您可以在任何支持 injectionContext 的函数中使用 vue-query 组合式函数。例如,路由器导航守卫。 使用此新功能时,请确保 vue-query 组合式函数在 effectScope 内运行。否则,可能会导致内存泄漏。 我们添加了 dev-only 警告以通知用户潜在的误用。