写在前面
React 生态中,SSR 支持做得最好的可能是 Next.js,但 SSR 并不是Next.js的全部,只是其提供的预渲染支持之一:
- SSG(Static Site Generation/Static Generation):静态生成,在编译时生成静态 HTML
- SSR(Server-Side Rendering):服务端渲染,用户请求到来时动态生成 HTML
通过各种方式在 CSR 开始之前预先渲染出页面内容,从而加快首屏性能,同时满足 SEO 的需要,这正是 Next.js 最核心的特性
不仅如此,Next.js 还提供了混用支持,能够将不同渲染模式结合使用,融合互补,例如:
- ISR(Incremental Static Regeneration):增量静态再生成,运行时定期重新生成静态 HTML
- SSG 降级 SSR:未命中预先生成的静态 HTML 时,立即进行 SSR
- SSR 带静态缓存:SSR 完成之后,将结果缓存起来,下次命中静态缓存直接返回(相当于 SSG)
- SSG 结合 CSR:编译时生成静态部分(页面外框),CSR 填充动态部分(页面内容)
- SSR 联动 CSR:URL 直接访问走更快的 SSR,SPA 跳转过来走体验更优的 CSR
这些细腻的混合渲染支持让各种渲染模式得以充分发挥其优势,也让 Next.js 增色不少
SSG + SSR
SSG 相当于把 SSR 的渲染过程前移到了编译时,从而优化掉这部分耗时,达到极佳的页面加载性能。但也存在明显的缺陷——只能用来渲染静态内容,使得一个原本很厉害的方案很难有用武之地。那么,有没有办法扩大其适用场景?
有。关键在于如何理解“静态”,静态、动态实际上描述的是内容的变化频率,几乎(永远)不会变,或者变化频率很低的内容,我们称之为静态内容。所以只要想办法应对内容变化,就有可能把 SSG 的适用场景从经常不变的“静态内容”扩大到不经常变的“动态内容”
极限情况下,“不经常变”等价于“不是每一次都变”,也就是说,除了实时/个性化等每时每刻都动态变化的内容,其余场景都可以用 SSG,当然,前提是要保障内容能够按需要的频率更新生效。内容更新其实就是重新 SSG,所以只缺一个更新时机……
另一个不那么显而易见的限制是静态内容的数量,因为渲染工作要在编译时全部完成,如果静态数据有 100 万条,就要编译生成 100 万份 HTML,编一次可能需要好几天……编译成本(无论时间/机器)会随内容数量不断增加,这是 SSG 渲染模式与生俱来的问题,看起来是无解的。除非,编译时不生成全量页面……
而面向用户请求的 SSR 恰好能够提供合适的更新时机,同时作为编译的下游,SSR 有机会拦住漏网之鱼。于是,SSG 与 SSR 一拍即合,SSG 只编译生成小部分热点页面,其余的在运行时通过 SSR 生成。用户请求到来时,根据内容是否需要更新来决定该走 SSR 重新生成还是沿用上次生成的产物:
如此这般,SSG 扩大了适用场景(高频变化的内容、编不完的海量内容),SSR 获得了性能优势(静态缓存):
P.S.关于 SSG 与 SSR 结合的更多信息,见When is fallback: true useful?、Incremental Static Regeneration
SSG + CSR
与 SSR 相比,SSG 成本更低,本地编译生成静态 HTML,托管到 Web 服务器或 CDN 即可享受到预渲染带来的加载性能提升,没有应用服务器的高额机器成本,也不用担心 SSR 在线服务的可用性和运维工作
借助 SSR 扩大 SSG 的应用场景不得不考虑与之俱来的成本问题,那么,有没有成本更低的办法?
也有,但体验上要有所妥协。既然 SSG 擅长渲染静态内容,不妨对页面内容进行动静分离,将页面上静态的部分交由 SSG 编译生成,其余动态部分仍通过 CSR 来填充:
SSG 结合 CSR,既缩短了页面加载的白屏时间,又避免了 SSR 的额外成本。不过,美中不足的是加载体验不如纯 SSG,毕竟(用户可能更关心的)动态内容需要在客户端二次渲染才能呈现出来,不像 SSG 能够一次性呈现完整内容。因此,这种方式带来的更多是体验提升,用户感知上页面载入变快了,算是一种渐进式渲染模式
P.S.关于 SSG 与 CSR 结合的更多信息,见Fetching data on the client side
SSR + CSR
SSG、SSR、CSR 三者两两结合,最耐人寻味的可能是这第三种——SSR 结合 CSR
hydrate不算,SSR 与 CSR 还有结合点么?
当然有。SSR 能够有效缩短页面加载过程中的白屏时间,同时提供页面内容一次性完整呈现的畅快体验,与之相比,CSR 渲染性能依赖客户端环境、数据请求滞后等缺点变得无限大,大到掩盖了 CSR 的高光优势:
- 无刷新加载内容
- 可根据用户行为预加载
这些优势在首屏加载过程中确实体现不出来,所以单看页面加载性能的话,SSR 完胜 CSR,二者之中任选一个即可,没有结合的必要。然而,如果将视角提升到用户操作的全流程,我们发现 CSR 与 SSR 能够以非常融洽的方式完美结合:
- 首屏加载走 SSR:无论用户直接通过 URL 访问的是首页还是二级、三级页,SSR 都能让页面以最快的速度呈现出来
- 站内跳转走 CSR:之后交互操作中的页面跳转,通过 CSR 无缝加载新内容,甚至能够预测用户行为提前加载目标页的内容
即,首屏加载工作交给更快的 SSR 来做,交互过程中让 CSR 大展身手:
Next.js 不仅对这种结合方式提供了内置支持,还能够自动预加载可视区域中的站内链接:
P.S.关于 SSR 结合 CSR 的更多信息,见Only runs on server-side
有所得、有所惑,真好
关注「前端向后」微信公众号,你将收获一系列「用心原创」的高质量技术文章,主题包括但不限于前端、Node.js以及服务端技术
本文首发于 ayqy.net ,原文链接 http://www.ayqy.net/blog/next...