SSR:早期服务端渲染
在 web 应用早期时代,view 层仅仅是 web 架构中很轻量的一层,以 PHP,JSP 渲染为主。服务端拉取数据后结合静态模版渲染生成有数据内容的页面,并编写 javascript 脚本控制 UI 的交互。这种早期服务端渲染的方式,很简单,也有更好的首屏渲染和 SEO。但是这是一种多页面应用,切换路由需要刷新页面重新发起请求渲染新的视图,对用户体验来说很不友好。
优势:
- 架构简单
- 页面首屏渲染快
- 有利于 SEO
缺点:
- 多页面应用:切换路由需要刷新页面,用户体验差
CSR: 客户端渲染
由于多页面应用体验的不便,有没有办法在客户端实现路由机制,切换路由不刷新页面。借助浏览器 hash 或 history 模式实现客户端路由的单页面应用,页面全部交由前端渲染,通过异步请求和客户端进行数据传输,大大提高了页面切换的体验。客户端渲染更大的价值在于促成了前后端分离,各端可以专攻自己的领域,前端负责视图,后端负责数据。前端也开始渐渐发展了自己的生态。
优势:
- 前后端分离:各端负责自己的领域,为后续前端的发展创造了条件
- 单页面应用:切换路由无需刷新页面,用户体验好
缺点:
- 页面需加载所有运行时资源,并且异步获取数据,首屏渲染性能不佳
- 不利于 SEO
SSR: 现代服务端渲染
因为在客户端渲染的页面需加载所有运行时资源加载,并且异步获取数据,LCP 和 TTI 时间被大大延迟。
所以 web 应用又开始向服务端延展,结合了服务端和客户端各自的优势,将首屏渲染页面交由服务端渲染生成,客户端负责 hydrate,为 UI 添加交互行为。
hydrate 方式也从全量 hydrate 演变为 选择性 hydrate(Island Architecture) 可以为需要交互的 UI 优先 hydrate 提高 TTI 时间。
优势:
- 首屏渲染和用户交互时间快
- 有利于 SSO
缺点:
- 架构复杂,应用场景受限。
服务端组件:RSC(React Server Component)
React 3 年前推出了 RSC,使 React 的组件可以运行在服务端。充分的利用服务端的能力,低延迟请求,服务端环境,无捆绑第三方包,无状态管理等优势。并让有交互行为的组件继续运行在客户端。使不同的组件分工明确,有交互行为的动态组件运行在客户端,只负责展示的静态组件运行在服务端。
优势:
- 服务端环境 可以使用 fs 系统,第三方服务,数据库等
- 运行时组件 由客户端发起请求在服务端运行,只输出运行后的结果,返回给客户端进行渲染。
- 无捆绑包 客户端组件的运行环境是浏览器,需要依赖第三方库进行渲染。而服务器组件在运行时已经借助第三包输出最终的结果,所以没有第三包返回,大大减少组件体积。
- 自动代码分割 同样因为服务端组件输出的是运行后的结果,所以运行的过程中只加载需要引入的组件。
// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from './OldPhotoRenderer.js';
import NewPhotoRenderer from './NewPhotoRenderer.js';
function Photo(props) {
// Switch on feature flags, logged in/out, type of content, etc:
if (FeatureFlags.useNewPhotoRenderer) {
return <NewPhotoRenderer {...props} />;
} else {
return <OldPhotoRenderer {...props} />;
}
}
- 低延迟请求 组件所需要的数据由服务端发出,更低的网络延迟。
- 无状态管理 服务端组件的很简单,通过数据动态生成不同的组件抽象描述,无需状态管理
缺点:
- 前后端耦合,各端不能很好的被移接和替代。
总结:CSR 渲染 对客户端来说是一种大的运行时,需要提前加载所有需要运行的资源,初次渲染比较耗时,并且并不是所有的资源都会被用户使用到。而 RSC 则将渲染运行时转移至服务端,按需返回用户需要的资源,并且可以使组件职责分工明确,客户端组件负责交互,服务端组件负责处理数据并展示,充分利用各端的优势。并且 RSC 可以和 SSR 同时使用,加速首屏渲染和交互时间。