如何降低Next.js框架中SSR渲染时长 优化产品页加载性能
Next.js SSR 产品页加载耗时优化方案
现有代码问题
你的代码存在1个显性bug:catch块中直接引用了仅在try作用域内定义的result变量,请求报错时会触发未定义错误,导致SSR渲染失败。其次核心性能问题是未做缓存、全量拉取数据、每次分类切换都走全页SSR请求,数据量上来后耗时必然很高。
可落地优化方案
- 渲染策略适配
如果产品内容更新频率低于1分钟/次,优先用ISR(增量静态再生)替代SSR,提前静态生成高频访问的分类页、详情页,设置合理的revalidate刷新周期,用户访问时直接返回静态内容,后台异步更新数据,无需每次请求都拉接口渲染。
列表页分类切换改为客户端路由+客户端数据请求,不要每次切换都触发全页SSR刷新,配合SWR/React Query这类请求库,自动实现数据缓存、重试、预加载,同时可加骨架屏作为加载占位,避免页面长时间空白。
必要时开启next/link的预加载能力,用户hover分类链接时就提前预取对应页面数据,减少点击后的等待时长。 - 接口侧优化
列表页禁止一次性拉取1000条全量数据,新增分页/无限滚动逻辑,每次仅拉取当前可见区域的20-50条数据;接口返回时裁剪冗余字段,仅返回前端渲染需要的内容,大幅降低响应体积。
后端接口本身也可加查询缓存,相同分类、相同筛选条件的请求直接返回缓存结果,降低数据库查询压力。 - 多层缓存叠加
服务端侧:给SSR页面响应头设置Cache-Control: s-maxage=60, stale-while-revalidate=120,CDN/边缘节点可缓存页面内容,重复请求直接返回缓存。也可在Next.js服务端加内存缓存(比如用node-cache),相同产品ID的请求直接读缓存,无需重复调用后端接口。
前端侧:将用户访问过的分类、产品数据存入localStorage/sessionStorage,二次访问时先读缓存渲染,后台再异步拉取最新数据更新。
优化后代码示例
ISR版本(推荐用于产品详情页)
export async function getStaticProps(context) { const { id, categoryId } = context.params; try { const response = await fetch(Constants.getProducts, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ _id: id }), }); const result = await response.json(); return { props: { result: result.doc[0], categoryId, status: 200 }, revalidate: 60 // 60秒后后台自动刷新静态内容,用户无感知 } } catch(e) { return { props: { result: null, categoryId, status: 404 }, revalidate: 10 // 错误页10秒后重试生成 } } } // 需要配合getStaticPaths指定预生成的页面,或者开fallback实现动态生成 export async function getStaticPaths() { return { paths: [], // 可传入高频访问的产品ID提前生成 fallback: 'blocking' // 未预生成的页面首次访问时SSR生成,之后走静态缓存 } }
带缓存的SSR版本(必须用SSR场景)
import NodeCache from 'node-cache'; // 内存缓存,缓存有效期60秒 const productCache = new NodeCache({ stdTTL: 60 }); export async function getServerSideProps(context) { const { id, categoryId } = context.query; const cacheKey = `product_${id}`; let result = productCache.get(cacheKey); // 缓存命中直接返回 if (result) { return { props: { result: result.doc[0], categoryId, status: 200 } } } try { const response = await fetch(Constants.getProducts, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ _id: id }), }); result = await response.json(); // 写入缓存 productCache.set(cacheKey, result); // 加HTTP缓存,CDN可直接缓存页面 context.res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate=120'); return { props: { result: result.doc[0], categoryId, status: 200 } } } catch(e) { return { props: { result: null, categoryId, status: 404 } } } }
内容的提问来源于stack exchange,提问作者Pooja
相关产品推荐
相关产品推荐

