@ equinor/ 聚变- query
反应数据取取和缓存库建于RxJS可观测器上,有队列策略,自动重试,以及综合事件系统.
何时使用
使用 @equinor/fusion-query 当您需要时:
- 用内置缓存获取远程数据, 防止冗余的网络请求
- 以可配置的队列策略管理同时提出的请求( E)
switch, (中文(简体) ),merge, (中文(简体) ),concat(中文(简体) ) - 以自定义的延迟和计数方式自动重试失败的请求
- 对记录、调试或遥测的生命周期事件进行查询的反应
- 通过共享共享在多个消费者之间共享缓存数据
QueryCache - 执行乐观的更新和缓存突变
安装
pnpm add @equinor/fusion-query
快速启动
import { Query } from '@equinor/fusion-query';
interface User {
id: string;
name: string;
}
const userQuery = new Query<User, { id: string }>({
client: {
fn: async (args, signal) => {
const res = await fetch(`/api/users/${args.id}`, { signal });
if (!res.ok) throw new Error('Failed to fetch user');
return res.json();
},
},
key: (args) => args.id,
expire: 60_000, // cache valid for 60 seconds
});
// Observable usage
userQuery.query({ id: '123' }).subscribe({
next: (result) => console.log(result.value),
error: (err) => console.error(err),
});
// Async/await usage
const result = await userQuery.queryAsync({ id: '123' });
console.log(result.value);
核心概念
查询
这个 Query 类是主要切入点。 协调 QueryClient (手取来),a QueryCache (存储结果),和一个队列运算符(控制货币). 何时 query() ,它首先检查缓存;如果存在有效数据,它立即发出。 否则,新抓取将被排队 .
查询客户端
这个 QueryClient 管理获取功能的执行,包括发送请求、处理故障复试和排放生命周期事件。 可以在多个领域共享 Query 实例.
查询缓存
这个 QueryCache 存储以生成缓存密钥键获取结果。 它支持插入,突变,无效,修饰,并重置. 多个 Query 实例可以共享一个用于跨组件数据共享的缓存 .
查询任务
A 类 QueryTask 代表单一的飞行中获取操作。 任务由缓存密钥来分解:如果两个消费者同时请求相同的数据,他们共享相同的任务.
配置选项
这个 QueryCtorOptions 类型定义 a 的所有配置 Query 实例:
| 选项 | 类型 | 说明 |
|---|---|---|
client | QueryClient | { fn, options? } | 获取函数或已有 QueryClient 实例 |
key | (args) => string | 从查询参数生成一个唯一的缓存密钥 |
expire | number | 在缓存项被视为 stale 之前的毫秒 |
validate | (entry, args) => boolean | 自定义用于验证缓存项的功能 |
cache | QueryCache | QueryCacheCtorArgs | 共享缓存实例或构建器参数 |
queueOperator | 'switch' | 'merge' | 'concat' | fn | 处理同时提出的请求的战略 |
队列策略
队列战略确定如何处理多次同时提出的不同数据请求:
switch(默认):当新的请求到达时取消活动请求。 最好的搜索 - you - 类型,只有最近的结果才重要.merge: 并行运行所有请求。 最好每个请求都必须独立解决.concat: 队列按顺序请求. 当执行命令重要时最好.
const query = new Query<SearchResult, { term: string }>({
client: { fn: searchApi },
key: (args) => args.term,
queueOperator: 'switch', // cancel stale searches
});
快取管理
变异( 优化更新)
直接更新缓存数据而不进行调试. 这个 mutate 方法返回恢复上一个值的倒数函数:
const undo = userQuery.mutate(
{ id: '123' },
(current) => ({ value: { ...current!, name: 'Updated Name' }, updated: Date.now() }),
);
// Roll back if the server update fails
try {
await updateUserOnServer('123', { name: 'Updated Name' });
} catch {
undo();
}
无效
将特定条目或整个缓存标为 stale, 强制在下一个查询上重排 :
userQuery.invalidate({ id: '123' }); // invalidate one entry
userQuery.invalidate(); // invalidate all entries
自定义缓存验证
以自定义逻辑覆盖默认过期验证 :
const query = new Query<Data, Args>({
client: { fn: fetchData },
key: (args) => JSON.stringify(args),
validate: (entry, args) => {
// Consider invalid if older than 30 minutes
return (entry.updated ?? 0) + 30 * 60_000 > Date.now();
},
});
共享缓存
多个 Query 实例可以共享 a QueryCache 避免在组件之间重复获取:
import { Query } from '@equinor/fusion-query';
import { QueryCache } from '@equinor/fusion-query/cache';
const sharedCache = new QueryCache();
const homepageQuery = new Query({
client: { fn: fetchPosts },
cache: sharedCache,
key: () => 'posts',
});
const sidebarQuery = new Query({
client: { fn: fetchPosts },
cache: sharedCache,
key: () => 'posts', // same key — reuses cached data
});
持续查询
使用 persistentQuery 当 UI 应实时反映缓存突变 。 与 query()在结果公布后完成的, persistentQuery() 随时更新默认的缓存条目, 继续打开订阅并重寄:
userQuery.persistentQuery({ id: '123' }).subscribe((result) => {
renderUser(result.value); // re-renders on cache mutation
});
事件系统
这个 Query.event$ 可观察的集合事件来自三个来源——"查询"本身,"查询卡通"和"查询卡通"——进入一流,用于监测和调试.
查询事件
| 活动 | 说明 |
|---|---|
query_created | 启动新查询 |
query_completed | 查询完成( 从缓存或获取) |
query_connected | 订阅者加入了现有的飞行任务 |
query_queued | 处理队列中添加了一个任务 |
query_aborted | 一个查询通过 AbortSignal |
query_cache_hit | 找到有效的缓存数据 |
query_cache_miss | 没有有效的缓存; 需要获取 |
query_cache_added | 获取的数据被写入到缓存 |
query_job_created | 创建了新的抓取工作 |
query_job_selected | 队列操作员选中了任务 |
query_job_started | 一个工作开始执行 |
query_job_closed | 工作报名被关闭了 |
query_job_completed | 工作完成且结果被缓存 |
query_job_skipped | 任务被跳过( 不再被观察到) |
查询快捷事件
| 活动 | 说明 |
|---|---|
query_client_job_requested | 请求带参数获取 |
query_client_job_executing | 获取已启动的执行 |
query_client_job_completed | 成功获取完成 |
query_client_job_failed | 获取执行失败 |
query_client_job_canceled | 获取已取消 |
query_client_job_error | 发生了无法弥补的错误 |
查询缓存事件
| 活动 | 说明 |
|---|---|
query_cache_entry_set | 设置了缓存项 |
query_cache_entry_inserted | 插入了一个新的条目 |
query_cache_entry_removed | 已删除一个条目 |
query_cache_entry_invalidated | 条目无效 |
query_cache_entry_mutated | 一个条目已变异 |
query_cache_trimmed | 缓存被修剪了 |
query_cache_reset | 缓存被重置 |
订阅事件
import { filter } from 'rxjs';
import { QueryEvent } from '@equinor/fusion-query';
import { QueryClientEvent } from '@equinor/fusion-query/client';
import { QueryCacheEvent } from '@equinor/fusion-query/cache';
// All events
query.event$.subscribe((event) => console.log(event.type, event));
// Filter by event type
query.event$.pipe(
filter((event) => event.type === 'query_cache_hit'),
).subscribe((event) => console.log('Cache hit:', event.data));
// Filter by event source using instanceof
const cacheEvents$ = query.event$.pipe(filter((e) => e instanceof QueryCacheEvent));
const clientEvents$ = query.event$.pipe(filter((e) => e instanceof QueryClientEvent));
const queryEvents$ = query.event$.pipe(filter((e) => e instanceof QueryEvent));
反应集成
这个 @equinor/fusion-query/react 子路径导出 useDebounceQuery 在 React 组件中获取已卸载数据的钩 :
import { useDebounceQuery } from '@equinor/fusion-query/react';
function SearchComponent() {
const [term, setTerm] = useState('');
const result = useDebounceQuery(myQuery, {
args: [{ search: term }],
delay: 300,
});
return <input value={term} onChange={(e) => setTerm(e.target.value)} />;
}
运算符
这个 operators namespace 提供 RxJS 操作器工具 :
switchQueue: 当新项目到达时取消在建工程(默认策略).mergeQueue: 不取消同时处理项目.concatQueue:按FIFO顺序依次处理项目.queryValue: 提取原始数据value从查询结果流,去除元数据.
import { operators } from '@equinor/fusion-query';
query.query({ id: '123' }).pipe(operators.queryValue).subscribe((user) => {
console.log(user.name); // plain User object, no wrapper
});
清理
总是不订阅查询可观察或调用 complete() 释放资源:
const sub = query.query(args).subscribe((r) => console.log(r));
// Later:
sub.unsubscribe();
// Or dispose the entire Query instance:
query.complete();
子路径导出
| 路径 | 目录 |
|---|---|
@equinor/fusion-query | Query,类型,运算符,事件 |
@equinor/fusion-query/cache | QueryCache, (中文(简体) ), QueryCacheEvent,缓存类型 |
@equinor/fusion-query/client | QueryClient, (中文(简体) ), QueryClientError, (中文(简体) ), QueryClientEvent,客户类型 |
@equinor/fusion-query/operators | 队列运算符和 queryValue |
@equinor/fusion-query/react | useDebounceQuery 钩子 |