logologo
指南
实践
配置
插件
案例
博客
生态
Module Federation Examples
Practical Module Federation
Zephyr Cloud
Nx
简体中文
English
指南
实践
配置
插件
案例
博客
Module Federation Examples
Practical Module Federation
Zephyr Cloud
Nx
简体中文
English
logologo

开始

介绍
设置环境
快速上手
功能导航
名词解释
npm 包

基础

运行时

Runtime 接入
Runtime API
Runtime Hooks
Rsbuild Plugin
Rspack 插件
Webpack Plugin
Rspress Plugin
Vite Plugin
Metro
类型提示
命令行工具
样式隔离

数据管理

数据获取
数据缓存
Prefetch

框架

Modern.js
Next.js

部署

使用 Zephyr Cloud 部署

调试

开启调试模式
Chrome Devtool
全局变量

Troubleshooting

概览

运行时

RUNTIME-001
RUNTIME-002
RUNTIME-003
RUNTIME-004
RUNTIME-005
RUNTIME-006
RUNTIME-007
RUNTIME-008
RUNTIME-009

构建

BUILD-001
BUILD-002

类型

概览
TYPE-001
其他
Edit this page on GitHub
Previous Page数据获取
Next PagePrefetch

#数据缓存

cache 函数可以让你缓存数据获取或计算的结果,它提供了更精细的数据粒度控制,并且适用于客户端渲染(CSR)、服务端渲染(SSR)、API 服务(BFF)等多种场景。

#基本用法

import { cache } from '@module-federation/bridge-react';

export type Data = {
  data: string;
};

export const fetchData = cache(async (): Promise<Data> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: `[ provider ] fetched data: ${new Date()}`,
      });
    }, 1000);
  });
});

#参数

  • fetchData: DataLoader 中的 fetchData 函数。
  • options(可选): 缓存配置
    • tag: 用于标识缓存的标签,可以基于这个标签使缓存失效
    • maxAge: 缓存的有效期 (毫秒)
    • revalidate: 重新验证缓存的时间窗口(毫秒),与 HTTP Cache-Control 的 stale-while-revalidate 功能一致
    • getKey: 简化的缓存键生成函数,根据函数参数生成缓存键
    • onCache: 缓存数据时的回调函数,用于自定义缓存数据的处理逻辑 options 参数的类型如下:
interface CacheOptions {
  tag?: string | string[];
  maxAge?: number;
  revalidate?: number;
  getKey?: <Args extends any[]>(...args: Args) => string;
  onCache?: (info: CacheStatsInfo) => boolean;
}

#返回值

cache 函数会返回一个新的 fetchData 函数,该函数有缓存的能力,多次调用该函数,不会重复执行。

#使用范围

仅支持在 DataLoader 中使用。

#详细用法

#maxAge

每次计算完成后,框架会记录写入缓存的时间,当再次调用该函数时,会根据 maxAge 参数判断缓存是否过期,如果过期,则重新执行 fn 函数,否则返回缓存的数据。

import { cache, CacheTime } from '@module-federation/bridge-react';

const getDashboardStats = cache(
  async () => {
    return await fetchComplexStatistics();
  },
  {
    maxAge: CacheTime.MINUTE * 2,  // 在 2 分钟内调用该函数会返回缓存的数据
  }
);

#revalidate

revalidate 参数用于设置缓存过期后,重新验证缓存的时间窗口,可以和 maxAge 参数一起使用,类似与 HTTP Cache-Control 的 stale-while-revalidate 模式。

如以下示例,在缓存未过期的 2分钟内,如果调用 getDashboardStats 函数,会返回缓存的数据,如果缓存过期,2分到3分钟内,收到的请求会先返回旧数据,然后后台会重新请求数据,并更新缓存。

import { cache, CacheTime } from '@module-federation/bridge-react';

const getDashboardStats = cache(
  async () => {
    return await fetchComplexStatistics();
  },
  {
    maxAge: CacheTime.MINUTE * 2,
    revalidate: CacheTime.MINUTE * 1,
  }
);

#tag

tag 参数用于标识缓存的标签,可以传入一个字符串或字符串数组,可以基于这个标签使缓存失效,多个缓存函数可以使用一个标签。

import { cache, revalidateTag } from '@module-federation/bridge-react';

const getDashboardStats = cache(
  async () => {
    return await fetchDashboardStats();
  },
  {
    tag: 'dashboard',
  }
);

const getComplexStatistics = cache(
  async () => {
    return await fetchComplexStatistics();
  },
  {
    tag: 'dashboard',
  }
);

revalidateTag('dashboard-stats'); // 会使 getDashboardStats 函数和 getComplexStatistics 函数的缓存都失效

#getKey

getKey 参数用于自定义缓存键的生成方式,例如你可能只需要依赖函数参数的一部分来区分缓存。它是一个函数,接收与原始函数相同的参数,返回一个字符串作为缓存键:

import { cache, CacheTime } from '@module-federation/bridge-react';
import { fetchUserData } from './api';

const getUser = cache(
  async (userId, options) => {
    // 这里 options 可能包含很多配置,但我们只想根据 userId 缓存
    return await fetchUserData(userId, options);
  },
  {
    maxAge: CacheTime.MINUTE * 5,
    // 只使用第一个参数(userId)作为缓存键
    getKey: (userId, options) => userId,
  }
);

// 下面两次调用会共享缓存,因为 getKey 只使用了 userId
await getUser(123, { language: 'zh' });
await getUser(123, { language: 'en' }); // 命中缓存,不会重新请求

// 不同的 userId 会使用不同的缓存
await getUser(456, { language: 'zh' }); // 不会命中缓存,会重新请求

你也可以使用 Modern.js 提供的 generateKey 函数配合 getKey 生成缓存的键:

INFO

Modern.js 中的 generateKey 函数确保即使对象属性顺序发生变化,也能生成一致的唯一键值,保证稳定的缓存

import { cache, CacheTime, generateKey } from '@module-federation/bridge-react';
import { fetchUserData } from './api';

const getUser = cache(
  async (userId, options) => {
    return await fetchUserData(userId, options);
  },
  {
    maxAge: CacheTime.MINUTE * 5,
    getKey: (userId, options) => generateKey(userId),
  }
);

#customKey

customKey 参数用于定制缓存的键,它是一个函数,接收一个包含以下属性的对象,返回值必须是字符串或 Symbol 类型,将作为缓存的键:

  • params:调用缓存函数时传入的参数数组
  • fn:原始被缓存的函数引用
  • generatedKey:框架基于入参自动生成的原始缓存键
INFO

一般在以下场景,缓存会失效:

  1. 缓存的函数引用发生变化
  2. 函数的入参发生变化
  3. 不满足 maxAge
  4. 调用了 revalidateTag

customKey 可以用在函数引用不同,但希望共享缓存的场景,如果只是自定义缓存键,推荐使用 getKey

这在某些场景下非常有用,比如当函数引用发生变化时,但你希望仍然返回缓存的数据。

import { cache } from '@module-federation/bridge-react';
import { fetchUserData } from './api';

// 不同的函数引用,但是通过 customKey 可以使它们共享一个缓存
const getUserA = cache(
  fetchUserData,
  {
    maxAge: CacheTime.MINUTE * 5,
    customKey: ({ params }) => {
      // 返回一个稳定的字符串作为缓存的键
      return `user-${params[0]}`;
    },
  }
);

// 即使函数引用变了,只要 customKey 返回相同的值,也会命中缓存
const getUserB = cache(
  (...args) => fetchUserData(...args), // 新的函数引用
  {
    maxAge: CacheTime.MINUTE * 5,
    customKey: ({ params }) => {
      // 返回与 getUserA 相同的键
      return `user-${params[0]}`;
    },
  }
);

// 即使 getUserA 和 getUserB 是不同的函数引用,但由于它们的 customKey 返回相同的值
// 所以当调用参数相同时,它们会共享缓存
const dataA = await getUserA(1);
const dataB = await getUserB(1); // 这里会命中缓存,不会再次发起请求

// 也可以使用 Symbol 作为缓存键(通常用于共享同一个应用内的缓存)
const USER_CACHE_KEY = Symbol('user-cache');
const getUserC = cache(
  fetchUserData,
  {
    maxAge: CacheTime.MINUTE * 5,
    customKey: () => USER_CACHE_KEY,
  }
);

// 可以利用 generatedKey 参数在默认键的基础上进行修改
const getUserD = cache(
  fetchUserData,
  {
    customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
  }
);

#onCache

onCache 参数允许你跟踪缓存统计信息,例如命中率。这是一个回调函数,接收有关每次缓存操作的信息,包括状态、键、参数和结果。你可以在 onCache 返回 false 来阻止命中缓存。

import { cache, CacheTime } from '@module-federation/bridge-react';

// 跟踪缓存统计
const stats = {
  total: 0,
  hits: 0,
  misses: 0,
  stales: 0,
  hitRate: () => stats.hits / stats.total
};

const getUser = cache(
  fetchUserData,
  {
    maxAge: CacheTime.MINUTE * 5,
    onCache({ status, key, params, result }) {
      // status 可以是 'hit'、'miss' 或 'stale'
      stats.total++;

      if (status === 'hit') {
        stats.hits++;
      } else if (status === 'miss') {
        stats.misses++;
      } else if (status === 'stale') {
        stats.stales++;
      }

      console.log(`缓存${status === 'hit' ? '命中' : status === 'miss' ? '未命中' : '陈旧'},键:${String(key)}`);
      console.log(`当前命中率:${stats.hitRate() * 100}%`);
    }
  }
);

// 使用示例
await getUser(1); // 缓存未命中
await getUser(1); // 缓存命中
await getUser(2); // 缓存未命中

onCache 回调接收一个包含以下属性的对象:

  • status: 缓存操作状态,可以是:
    • hit: 缓存命中,返回缓存内容
    • miss: 缓存未命中,执行函数并缓存结果
    • stale: 缓存命中但数据陈旧,返回缓存内容同时在后台重新验证
  • key: 缓存键,可能是 customKey 的结果或默认生成的键
  • params: 传递给缓存函数的参数
  • result: 结果数据(来自缓存或新计算的)

这个回调只在提供 options 参数时被调用。当使用无 options 的缓存函数时,不会调用 onCache 回调。

onCache 回调对以下场景非常有用:

  • 监控缓存性能
  • 计算命中率
  • 记录缓存操作
  • 实现自定义指标

#存储

目前不管是客户端还是服务端,缓存都存储在内存中,默认情况下所有缓存函数共享的存储上限是 1GB,当达到存储上限后,使用 LRU 算法移除旧的缓存。

INFO

考虑到 cache 函数缓存的结果内容不会很大,所以目前默认都存储在内存中

可以通过 configureCache 函数指定缓存的存储上限:

import { configureCache, CacheSize } from '@module-federation/bridge-react';

configureCache({
  maxSize: CacheSize.MB * 10, // 10MB
});