原文 写于 2021/6/5 。
概要
SSR 的主要任务:在 JS 代码加载前就能让用户看到页面内容。
React 中 SSR 的执行步骤:
- (在服务端)为 整个应用 获取数据。
- (在服务端)将 整个应用 渲染成 HTML 并作为响应发送出去。
- (在客户端)为 整个应用 加载 JS 代码。
- (在客户端)将 JS 代码与 HTML 关联起来(即 hydration)。
但要注意的是 每个步骤都需要等待上一步执行完才可执行 ,因此每个步骤都有可能是整个应用加载慢的原因。
而 React 18 允许你通过 <Suspense>
将 整个应用拆解为不相关联的小单元 ,每个单元都可以单独执行以上的步骤且不会阻塞应用的其它部分。通过这一方式可以让应用显示得更快且更早变得可交互,一些加载慢的内容并不会牵连加载快的内容。这一特性也让 React.lazy
更适合于 SSR 。
SSR 是什么
SSR 可以先在服务端,将 React 组件渲染为 HTML 发送给用户,保证用户可以先看到页面(尽管这样的 HTML 是不可交互的)。而若没有 SSR 技术,在 JS 代码加载前用户通常只会看到白屏(一个空白的 HTML 模版),这对用户是不友好的。
在 JS 代码中,React 也会加载组件树,但并不是为了生成 DOM 节点,而是将所有交互逻辑直接挂载到先前的 HTML 上。这一步被称为 hydration(水合),表示 HTML 就像“吸了水”一样变得可交互了。在此之后,组件可以设置状态、事件也可以进行响应了,等等。
SSR 这一首屏加载的特性对网络较差的用户非常友好,因为无需等到 JS 代码执行就能看到页面。同时也有助于提升搜索引擎排名。
不要将 SSR 和服务端组件搞混了,两者是互补的关系。服务端组件是一个实验性功能,且并不会成为 React 18 初版的一部分。点击 此处 了解更多有关于服务端组件的内容。
当前 SSR 的弊端
上述方法虽然可行,但在许多方面并不是最优的:
- 在渲染之前你需要获取完所有数据,这意味着如果 API 很慢的话会拖累后续渲染 HTML 的步骤。
- 在任何 hydrate 前,必须先加载完组件树。这意味着子组件不能单独提前变得可交互。
- 只有当 hydrate 完全结束后才可变得可交互。这是 React 自身的设计问题。一些组件如果很晚才变得可交互对于用户来说可能是致命的,比如导航栏。
但总的来说,一切弊端归因于前文说的 SSR 执行步骤,因为每一个执行步骤都是阻塞的,且粒度都是整个应用。而 React 18 希望借助 Suspense
将 SSR 的执行粒度从整个应用降低为组件。
React 18 带来的改进
通过 Suspense
可以解锁 React 18 带来的两项 SSR 特性:
- 在服务端上 流式渲染 HTML 。需搭配使用
renderToPipeableStream
方法。 - 在客户端上 选择性 hydration 。需在客户端使用
hydrateRoot
方法,并利用Suspense
将应用拆为几个部分。
流式渲染
浏览器在接收 HTML 数据的过程中,会先将可解析的内容展示出来,React 的流式渲染则利用了这一特性。
可以通过 Suspense
将比较慢的组件拆分出来,React 会在需要时先在 HTML 中展示 fallback 值,等该慢组件加载完后再通过传输 <script>
代码