跳到主要内容

服务端缓存与取消

cache 在服务端组件中缓存什么?​

  • 函数包装: cache(fn) 返回记忆化函数,相同参数的调用可以共享计算结果或同一个异步 Promise;
  • 请求范围: React 在不同服务端请求之间失效缓存,它不是跨用户、跨请求的永久数据库缓存;
  • 声明复用: 在模块层创建并导出同一个包装函数,多次调用 cache(fn) 会产生互不共享的缓存;
import { cache } from "react";

export const getUser = cache(async (id) => {
return database.users.find(id);
});

async function Profile({ id }) {
const user = await getUser(id);
return <h1>{user.name}</h1>;
}

为什么看似相同参数没有命中缓存?​

  • 参数身份: 参数逐项按 Object.is 比较,每次新建的对象不是同一个参数,优先传稳定 ID 等原始值;
  • 调用上下文: 缓存能力依赖服务端 React 渲染上下文,在外部普通代码中调用无法使用缓存;
  • 错误结果: 同组参数对应的抛错也会被缓存,不能假定再次调用必然重新尝试请求;

如何提前启动同一份服务端数据请求?​

  • 预加载: 在确定将需要数据时先调用缓存函数启动工作,之后在消费组件 await 同一参数;
const getUser = cache(async (id) => {
return await db.user.query(id);
});

async function Profile({ id }) {
const user = await getUser(id);
// ...
}

function Page({ id }) {
getUser(id);

return <Profile id={id} />;
}

cacheSignal 如何终止不再需要的工作?​

  • 当前仅用于服务器组件;
  • 生命周期: 在服务端渲染中调用 cacheSignal 得到 AbortSignal,渲染完成、失败或中止后取消不再需要的进行中工作;
  • 空值情况: 渲染外和当前 Client Component 环境返回 null,不适用 cache 的生命周期;
import { cache, cacheSignal } from "react";

const load = cache(async (url) => {
const response = await fetch(url, { signal: cacheSignal() });
if (!response.ok) throw new Error("请求失败");
return response.json();
});