Skip to content

同构渲染的发展史

Posted on:2023年11月4日 at 10:24

前言

由于前端大量技术栈的出现与更迭,很容易让人应接不暇。我学习Next的初衷也只是因为React官网首推换成了Next,说难听点就是为学而学。

直到有一天翻我们专业公众号的历史文章,看了一些远古师兄师姐的分享,其中有这样一句话

每学习一个新东西的时候你要思考他为什么会出现,出现的意义是什么,解决了什么痛点,跟同类技术用什么不同,他们之间又有什么联系。

故幡然醒悟,去了解了同构渲染的发展史与原理。本篇文章记录个人理解的同构渲染发展史。

SSR/RSC 的产生

Web页面渲染发展历程

SSR基本原理

  1. 服务端根据路由找到要渲染的component
  2. 服务端将页面渲染成HTML(string / stream)
  3. 服务端根据component中预定义的数据预取方法请求数据
  4. 服务端将数据序列化拼接到HTML中客户端接收到服务器响应,渲染收到的HTML(以及CSS等)
  5. 用户可正常浏览非交互式页面
  6. 执行hydration(水化),激活页面element的事件监听方法
  7. 用户可正常交互

SSR存在的问题

这些步骤是连续的和阻塞的,这意味着服务器只能在获取所有数据后呈现页面的HTML。而且,在客户端上,React只有在下载了页面中所有组件的代码后才能对UI进行水化。如果页面有多个请求同时向后端获取数据,只要有一个接口未返回,页面就不会进行加载,整个程序就会处于待响应状态。

Suspense(悬念)

为了解决上述问题,React创建了Suspense,它允许在服务器端进行HTML流式传输,并在客户端上进行选择性的注入。通过将组件包装在 <Suspense>中,我们可以告诉服务器将该组件的渲染和注入降低优先级,让其他组件在不受较重组件阻塞的情况下加载。

使用suspense包裹组件后的页面加载

React Server Components (RSC)

React Server Components 是 React 团队在 React18.0 推出的一种新型组件。它们提供了服务端和客户端渲染的无缝结合,允许开发人员在服务器上渲染部分应用程序,同时保持客户端应用程序的交互性。

客户端组件 vs 服务端组件

Client ComponentServer Component
在客户端上运行的组件,通常在web浏览器中。
该组件可以访问DOM(文档对象模型)、浏览器api(如localstorage)、交互事件(如onClick)等,
而服务器无法访问这些组件。负责处理用户交互和更新用户界面。
在服务器上获取和渲染的组件。
它们与传统的React组件类似,但在服务器而不是客户端上执行。
这意味着它们可以访问服务器的全部功能,并且可以执行在客户机上无法执行的任务,例如数据库查询。

img

RSC 渲染流程

img

Next.js做了什么?

RSC + Suspense => Streaming(流式渲染)

Next 13.0引入的流式渲染

使用流式渲染后的页面渲染过程,可以理解为将水合过程加入到了Suspense中,使得交互过程也可以分部注入

SSR带来的问题

黑暗模式闪烁

暗黑主题的实现,通常是把主题变量存在到 localStorage 中。当页面渲染的时候从 localStorage 中读取暗黑主题的变量同时设置对应的 class 为 dark。

如果页面是 CSR 模式那么这里是没有问题的,页面也不会有闪动。但是如果页面是 SSR 渲染的,服务端获取不到localstorage,就会存在闪动的问题。

把两个渲染流程分开来看:

因为存在这两个阶段,因此如果页面已经被设置为暗黑模式了,那么页面将会发生 服务端渲染返回的白色底色 -> 客户端渲染返回的暗黑底色 这样的转变。

闪屏gif

试了试useLayoutEffect等钩子都会有明显闪屏,选择使用参考该文章的解决方法,在渲染body时执行脚本,返回带有主题类名的HTML字符串

从 React 最新文档中学习 SSR 下如何使用暗黑模式 - 掘金 (juejin.cn)

img

添加脚本后的页面

使用后,由于服务端与客户端渲染出的HTML不同(一个html类名中带有dark,另一个没有),会报出水合错误的警告。

在StackOverflow和GitHub上找了一些解决方法,使用cookie有点麻烦了,还是直接取消警告简单(

因为只会取消该元素(这里是html)的水合警告,所以大概不会出现安全问题。