SSR指的是服务端渲染,直出html到客户端,减少首屏白屏时间,这里不对SSR过多说明,有兴趣自行查找。
hydration
react服务端渲染需要经历以下的步骤
-
服务器请求数据
-
将数据渲染成html,输出到客户端
-
客户端加载js代码
-
将js逻辑与服务器生成的html绑定
渲染组件和事件绑定的整个处理过程叫做 hydration。但是整个过程是串行的,必须要等上一步完成才能执行下一步操作。如果项目规模大,某一部分组件生成比较慢,会导致整个网站渲染慢,那体验就很差
React18会使用Suspense来将你的应用程序分解成较小的独立单元。这些单元将独立完成这些步骤,并且不会阻碍应用程序的其他部分。因此,你的应用程序的用户将更快地看到内容,并能更快地开始与应用程序交互。应用程序中最慢的部分不会拖累那些较快的部分。这些优化在react18内部是自动完成的
Suspense
既然SSR整个过程是依赖强相关的,react18需要解决两个问题
- 流式渲染,要使用此功能,必须要从原先的
renderToString
切换到pipeToNodeWritable
- 选择性hydration,在客户端使用
createRoot
方法,用<Suspense>
包装比较慢的组件(相对不那么重要的组件)
<Layout>
<NavBar />
<Sidebar />
<RightPane>
<Post />
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
</RightPane>
</Layout>
使用 <Suspense>
包装 <Comments />
组件,react在服务端渲染的时候,不需要等待comments组件完成渲染再返回给客户端,可以先展示加载,上面的代码最终会渲染成
<main>
<nav>
<!--NavBar -->
<a href="/">Home</a>
</nav>
<aside>
<!-- Sidebar -->
<a href="/profile">Profile</a>
</aside>
<article>
<!-- Post -->
<p>Hello world</p>
</article>
<section id="comments-spinner">
<!-- Spinner -->
<img width=400 src="spinner.gif" alt="Loading..." />
</section>
</main>
接下来服务器会继续准备comment组件的html,如果生成结束,就会将额外的html发送到同一个流中,并代入一个可执行的script
<div hidden id="comments">
<!-- Comments -->
<p>First comment</p>
<p>Second comment</p>
</div>
<script>
// This implementation is slightly simplified
document.getElementById('sections-spinner').replaceChildren(
document.getElementById('comments')
);
</script>
React.lazy
虽然可以提前发送html,但是首次渲染输出的js过大,也需要一些执行时间,通常在客户端渲染无非做两件事,代码拆分,按需加载。
React.lazy可以在服务端完成这些事了,我们把评论组件的代码从主包中分割出来。
import { lazy } from 'react';
const Comments = lazy(() => import('./Comments.js'));
// ...
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
这样react在除了comment组件之外的其他模块,都可以提前hydration,不需要等待所有代码加载完成再进行hydration
当多个Sunspend一起使用的时候,会出现一个问题,页面加载后优先hydration哪部份组件呢?
<Layout>
<NavBar />
<Suspense fallback={<Spinner />}>
<Sidebar />
</Suspense>
<RightPane>
<Post />
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
</RightPane>
</Layout>
现在有SideBar和Comments组件都被Suspense包裹,react默认会从较早发现的 Suspense 组件开始进行 hydration ,这个例子,是侧边栏
但是用户此时不对侧边进行交互,而优先对评论区域进行交互
React会记录点击,优先给评论区域进行 hydration ,当完成后,React“重放”记录的点击事件(通过再次派发),并让你的组件对互动做出反应。 接下来再继续给侧边栏进行 hydration
总结
React18为SSR提供了两个主要功能
流式渲染,尽早发送html,通过Suspense实现,Suspense组件生成的html和script将会延迟发送给客户端。并在正确位置执行
选择性hydration让你在HTML和JavaScript代码完全下载之前,尽早开始为你的应用程序进行hydration。它还优先为用户正在互动的部分进行hydration,创造一种即时hydration的错觉