For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/basic-features/render/ssr-cache.md.
  • 简体中文
  • 渲染缓存

    在开发应用时,有时我们会将计算结果进行缓存,例如使用 React useMemo、useCallback 等 Hook。通过缓存我们可以减少计算的次数来减少 CPU 资源占用,提高用户体验。

    Modern.js 支持将服务器端渲染(SSR)结果进行缓存,减少服务器每次请求时的计算和渲染时间,从而加速页面加载速度,提高用户体验。同时,缓存也能降低服务端负载,节省计算资源,提高用户访问速度。

    配置方式

    在应用中创建 server/cache.[t|j]s 文件,并导出 cacheOption 配置缓存即可开启 SSR 渲染缓存:

    server/cache.ts
    import type { CacheOption } from '@modern-js/server-runtime';
    
    export const cacheOption: CacheOption = {
      maxAge: 500, // ms
      staleWhileRevalidate: 1000, // ms
    };

    配置说明

    缓存配置

    缓存策略参考 stale-while-revalidate 进行实现。

    在 maxAge 时间内会直接返回缓存内容,超过 maxAge 但在 staleWhileRevalidate 内也会直接返回缓存内容,但同时会异步做一次重新渲染。

    Object 类型

    export interface CacheControl {
      maxAge: number;
      staleWhileRevalidate: number;
      customKey?: string | ((pathname: string) => string);
    }

    其中 customKey 为自定义缓存 key。默认情况下 Modern.js 将会把请求 pathname 作为 key 进行缓存,但在某些情况下这不能满足你的需求,开发者可以进行自定义。

    Function 类型

    export type CacheOptionProvider = (
      req: IncomingMessage,
    ) => Promise<CacheControl | false> | CacheControl | false;

    有时开发者需要通过 req 来自定义缓存 key,或者特定 URL 时缓存不生效,可以配置为函数的形式进行处理, 例如以下代码:

    server/cache.ts
    
    import type { CacheOption, CacheOptionProvider } from '@modern-js/server-runtime';
    
    const provider: CacheOptionProvider = (req) => {
      const { url, headers, ... } = req;
      if(url.includes('no-cache=1')) {
        return false;
      }
    
      const key = computedKey(url, headers, ...);
      return {
        maxAge: 500, // ms
        staleWhileRevalidate: 1000, // ms
        customKey: key,
      }
    }
    
    export const cacheOption: CacheOption = provider;

    Mapping 类型

    export type CacheOptions = Record<string, CacheControl | CacheOptionProvider>;

    有时开发者面对不同的路由需要应用不同的缓存策略。我们也提供一种映射的方式进行配置, 以下列代码为例:

    server/cache.ts
    import type { CacheOption } from '@modern-js/server-runtime';
    
    export const cacheOption: CacheOption = {
      '/home': {
        maxAge: 50,
        staleWhileRevalidate: 100,
      },
      '/about': {
        maxAge: 1000 * 60 * 60 * 24, // one day
        staleWhileRevalidate: 1000 * 60 * 60 * 24 * 2 // two day
      },
      '*': (req) => { // 若上述路由无法匹配,则会匹配到 '*'
        const { url, headers, ... } = req;
        const key = computedKey(url, headers, ...);
    
        return {
          maxAge: 500,
          staleWhileRevalidate: 1000,
          customKey: key,
        }
      }
    }
    • 路由 http://xxx/home 将会应用第一条规则。
    • 路由 http://xxx/about 将会应用第二条规则。
    • 路由 http://xxx/abc 将会应用最后一条规则。

    上述 /home 和 /about 将会作为模式进行匹配,这意味着 /home/abc 也会匹配上该规则。同时,你也可以在其中编写正则语法:/home/.+

    缓存容器

    默认情况下,Server 将会使用内存进行缓存。但通常情况下服务将会部署在 Serverless 容器上。每一次的服务访问可能都是一个新的进程,这样每次访问都不能应用缓存。

    因此,Modern.js 支持开发者自定义缓存容器,容器需实现接口 Container:

    export interface Container<K = string, V = string> {
      /**
       * Returns a specified element from the container. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Container.
       * @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.
       */
      get: (key: K) => Promise<V | undefined>;
    
      /**
       * Adds a new element with a specified key and value to the container. If an element with the same key already exists, the element will be updated.
       *
       * The ttl indicates cache expiration time.
       */
      set: (key: K, value: V, options?: { ttl?: number }) => Promise<this>;
    
      /**
       * @returns boolean indicating whether an element with the specified key exists or not.
       */
      has: (key: K) => Promise<boolean>;
    
      /**
       * @returns true if an element in the container existed and has been removed, or false if the element does not exist.
       */
      delete: (key: K) => Promise<boolean>;
    }

    以下面代码为例,开发者可实现一个 redis 缓存容器。

    import type { Container, CacheOption } from '@modern-js/server-runtime';
    
    class RedisContainer implements Container {
      redis = new Redis();
    
      async get(key: string) {
        return this.redis.get(key);
      }
    
      async set(key: string, value: string): Promise<this> {
        this.redis.set(key, value);
        return this;
      }
    
      async has(key: string): Promise<boolean> {
        return this.redis.has(key);
      }
    
      async delete(key: string): Promise<boolean> {
        return this.redis.delete(key);
      }
    }
    
    const container = new RedisContainer();
    
    export const customContainer: Container = container;
    
    export const cacheOption: CacheOption = {
      maxAge: 500, // ms
      staleWhileRevalidate: 1000, // ms
    };

    缓存标识

    当开启渲染缓存后,Modern.js 将通过响应头 x-render-cache 来标识当前请求的缓存状态。下面是一个响应示例:

    < HTTP/1.1 200 OK
    < Access-Control-Allow-Origin: *
    < content-type: text/html; charset=utf-8
    < x-render-cache: hit
    < Date: Thu, 29 Feb 2024 02:46:49 GMT
    < Connection: keep-alive
    < Keep-Alive: timeout=5
    < Content-Length: 2937

    x-render-cache 可能会有以下几种值:

    名称说明
    hit缓存命中,返回缓存内容
    stale缓存命中,但数据陈旧,返回缓存内容的同时做重新渲染
    expired缓存命中,重新渲染后返回渲染结果
    miss缓存未命中