共计 3787 个字符,预计需要花费 10 分钟才能阅读完成。
写在后面
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 从新生成还是沿用上次生成的产物:
Instead, you may statically generate a small subset of pages and use fallback: true for the rest. When someone requests a page that’s not generated yet, the user will see the page with a loading indicator. Shortly after, getStaticProps finishes and the page will be rendered with the requested data. From now on, everyone who requests the same page will get the statically pre-rendered page.
Inspired by stale-while-revalidate, background regeneration ensures traffic is served uninterruptedly, always from static storage, and the newly built page is pushed only after it’s done generating.
如此这般,SSG 扩充了实用场景(高频变动的内容、编不完的海量内容),SSR 取得了性能劣势(动态缓存):
This ensures that users always have a fast experience while preserving fast builds and the benefits of Static Generation.
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 来填充:
First, immediately show the page without data. Parts of the page can be pre-rendered using Static Generation. You can show loading states for missing data.
Then, fetch the data on the client side and display it when ready.
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 大展身手:
When you request this page directly, getServerSideProps runs at the request time, and this page will be pre-rendered with the returned props.
When you request this page on client-side page transitions through next/link or next/router, Next.js sends an API request to the server, which runs getServerSideProps. It’ll return JSON that contains the result of running getServerSideProps, and the JSON will be used to render the page. All this work will be handled automatically by Next.js, so you don’t need to do anything extra as long as you have getServerSideProps defined.
Next.js 不仅对这种联合形式提供了内置反对,还可能主动预加载可视区域中的站内链接:
prefetch – Prefetch the page in the background. Defaults to true. Any <Link /> that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing prefetch={false}.
P.S. 对于 SSR 联合 CSR 的更多信息,见 Only runs on server-side
有所得、有所惑,真好
关注「前端向后」微信公众号,你将播种一系列「用 心原创」的高质量技术文章,主题包含但不限于前端、Node.js 以及服务端技术
本文首发于 ayqy.net,原文链接 http://www.ayqy.net/blog/next…