logo
  • 指南
  • 配置
  • 插件
  • API
  • 示例
  • 社区
  • Modern.js 2.x 文档
  • 简体中文
    • 简体中文
    • English
    • 开始
      介绍
      快速上手
      版本升级
      名词解释
      技术栈
      核心概念
      页面入口
      构建工具
      Web 服务器
      基础功能
      路由
      路由基础
      配置式路由
      数据管理
      数据获取
      数据写入
      数据缓存
      渲染
      服务端渲染(SSR)
      服务端流式渲染(Streaming SSR)
      渲染缓存
      静态站点生成(SSG)
      渲染预处理 (Render Preprocessing)
      样式开发
      引入 CSS
      使用 CSS Modules
      使用 CSS-in-JS
      使用 Tailwind CSS
      HTML 模板
      引用静态资源
      引用 JSON 文件
      引用 SVG 资源
      引用 Wasm 资源
      调试
      数据模拟(Mock)
      网络代理
      使用 Rsdoctor
      使用 Storybook
      测试
      Playwright
      Vitest
      Jest
      Cypress
      路径别名
      环境变量
      构建产物目录
      部署应用
      进阶功能
      使用 Rspack
      使用 BFF
      基础用法
      运行时框架
      扩展 BFF Server
      扩展一体化调用 SDK
      文件上传
      跨项目调用
      优化页面性能
      代码分割
      静态资源内联
      产物体积优化
      React Compiler
      提升构建性能
      浏览器兼容性
      配置底层工具
      源码构建模式
      服务端监控
      Monitors
      日志事件
      指标事件
      国际化
      基础概念
      快速开始
      配置说明
      语言检测
      资源加载
      路由集成
      API 参考
      高级用法
      最佳实践
      自定义 Web Server
      专题详解
      模块联邦
      简介
      开始使用
      应用级别模块
      服务端渲染
      部署
      集成国际化能力
      常见问题
      依赖安装问题
      命令行问题
      构建相关问题
      热更新问题
      已下线功能
      📝 编辑此页面
      上一页服务端流式渲染(Streaming SSR)下一页静态站点生成(SSG)

      #渲染缓存

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

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

      Tip

      需要 x.43.0+

      #配置方式

      在应用中创建 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缓存未命中