SSR一定要把大JSON写进代码里吗?封面图

前端使用 SSR(Server-Side Rendering,服务端渲染)时,并不一定要在代码中直接引入大块 JSON。真正需要判断的是:这些数据是否会变化、体积有多大、是否和用户或路由相关、是不是首屏渲染必须依赖。

如果把大量 JSON 直接 import 到组件或业务代码里,短期看起来简单,但很容易带来几个问题:代码包变大、构建产物膨胀、数据更新不灵活、首屏 HTML 或 hydration 数据过重。SSR 的重点不是“把所有数据提前塞进页面”,而是让服务端在合适的时机拿到合适的数据,再把首屏真正需要的部分交给页面。

SSR为什么不等于把大JSON写进代码

SSR 的工作方式通常是:用户访问页面时,服务端根据路由、参数、登录态或业务规则获取数据,然后生成 HTML 返回给浏览器。这里的数据来源可以是 API、数据库、文件系统、CMS、缓存,也可以是构建期生成的静态结果。

也就是说,SSR 需要的是“渲染时可用的数据”,而不是“必须写死在代码里的 JSON”。如果数据比较大,或者经常变化,直接放进代码反而会让维护和性能都变差。

SSR数据流:代码、数据和页面渲染分层示意图

哪些情况下不应该直接引入大JSON

如果数据具备下面任意一种特征,就不建议直接写进前端业务代码:

  1. 数据体积较大,会明显增加 JS 包或服务端渲染产物体积。
  2. 数据更新频繁,不能每次变更都重新发版。
  3. 数据和用户、地区、权限、路由参数有关。
  4. 页面只需要其中一小部分字段,却把完整 JSON 全量注入。
  5. 数据不是首屏必要内容,可以延后加载。

这类数据更适合放在数据层,通过服务端请求、缓存、字段裁剪或分页等方式处理。

常见可选方案

不同数据适合不同处理方式,不能一刀切。

大JSON在SSR中的处理方案矩阵图

1. 服务端请求API

对于经常变化、依赖用户状态或业务实时性的内容,推荐在 SSR 阶段由服务端请求 API:

async function getServerSideProps(context) {
  const res = await fetch(`https://api.example.com/page-data?id=${context.params.id}`);
  const data = await res.json();

  return {
    props: {
      page: {
        title: data.title,
        summary: data.summary,
        items: data.items.slice(0, 20)
      }
    }
  };
}

这里的重点是:服务端拿到完整数据后,可以只把页面真正需要的字段传给前端,而不是把原始大 JSON 全量注入。

2. 构建期生成静态数据

如果数据变化很少,比如地区字典、小型配置、固定文案、分类映射,可以在构建期读取文件或接口,再生成静态页面。这类数据可以用 SSG 或 ISR 处理,而不必每次请求都重新拉取。

但要注意,只有“静态且体积可控”的数据才适合这样做。如果 JSON 很大,仍然应该拆分、按需读取或做分页。

3. 服务端缓存

当多个页面复用同一批数据,或者接口响应较慢时,可以在服务端增加缓存层。这样既能避免把大 JSON 写进代码,也能减少接口压力和 SSR 耗时。

常见缓存对象包括栏目数据、导航配置、商品基础信息、内容列表、筛选条件等。缓存时间可以根据业务实时性设置,例如几十秒、几分钟或按发布事件主动刷新。

4. 字段裁剪和分页

很多“大 JSON”并不是页面真的需要大,而是接口返回太全。例如列表页只需要标题、摘要、封面和链接,却返回了正文、评论、扩展字段和统计数据。

这时应该从接口层或服务端适配层做字段裁剪:

function pickListItem(item) {
  return {
    title: item.title,
    url: item.url,
    cover: item.cover,
    summary: item.summary
  };
}

列表型数据还应该分页或分块加载,避免一次性把几百条记录塞进首屏 HTML。

5. 非首屏数据客户端懒加载

不是所有内容都必须参与 SSR。评论区、相关推荐、更多筛选项、历史记录、复杂图表等,如果不是首屏关键内容,可以在页面加载后再由客户端请求。

这样做能减少服务端渲染压力,也能降低 HTML 和 hydration 数据体积,让首屏更轻。

什么时候可以直接引入JSON

直接引入 JSON 也不是绝对不能用。它适合以下场景:

  1. 数据很小,例如几十行配置。
  2. 数据几乎不变,例如固定枚举、语言包片段、小型规则表。
  3. 数据不包含用户隐私、权限差异或实时业务状态。
  4. 引入后不会明显增加构建体积和首屏负担。

换句话说,小型、稳定、通用的数据可以放进代码;大型、动态、个性化的数据应该放在数据层。

判断思路

可以用三个问题判断是否应该把 JSON 写进代码:

  1. 它会不会经常变? 会变,就不要写死进代码。
  2. 它是不是很大? 很大,就要拆分、裁剪、分页或缓存。
  3. 它是不是首屏必须? 不是首屏必须,就可以延后加载。

如果三个问题里有两个答案偏向“是”,就更应该避免直接 import 大 JSON。

总结

SSR 并不要求把大块 JSON 直接写进前端代码。更合理的做法是把代码、数据和渲染职责分开:代码负责组件和交互,服务端负责按请求获取和加工数据,缓存层负责复用高频数据,客户端负责加载非首屏内容。

真正影响体验的不是“有没有 JSON”,而是 JSON 放在哪里、传多少、什么时候传。只要控制好数据来源、字段体积、缓存策略和 hydration 数据量,SSR 就可以既保持首屏体验,又避免代码包被大 JSON 拖重。