- Published on
从 use client指令看Nextjs SSR RSC
背景
use client 是nextjs的一个指令,用于将组件转为客户端组件。从字面来看,可能会造成一些困惑。
比如:客户端组件就只能在客户端执行,服务端不会执行?客户端组件渲染类似spa,只在客户端执行渲染?第一次请求获取的客户端组件是没有内容的?
React SSR 基本原理及Hydrate水合
- 服务端调用
react-dom/server
包中的renderToString
将react组件内容渲染内容输出为 html字符串返回给客户端。 此时输出的全是html字符串,无法使用js,无法挂载事件,没有交互。 - hydrate 将结合服务端返回的 html静态内容和react组件中的js部分,给静态dom添加交互。其实是类似于 客户端react的
render
方法,也是在客户端进行,差异在于render
会忽略 root dom中现有的 dom,而hydrate
则会复用root dom中现有的内容,匹配检查。 - nextjs将上述两部分内容组合起来。最终返回给前端的html中有dom内容,水合部分的js会插入html中通过script引入。

Nextjs use client
use client
指令的组件会变成客户端组件,与之对应的是 服务器组件RSC(React Server Component) 。客户端组件可以在服务端和客户端两端都渲染,服务器组件只能在服务端渲染,不能在客户端渲染。服务器组件只会在服务端渲染一次,输出的结果是 html string。可以简单理解为一个nodejs中执行的函数,只不过也是以.jsx结尾,不要和客户端的react组件混淆,服务器组件是一个新的概念。
服务器组件 的好处是减少了js的体积,没有水合的js代码。特别是需要引入较大体积第三方库的组件,现在可以在服务器组件中在服务端生成,从而减少客户端加载的js体积。
服务器组件返回的html中也有会
bundle.js
,里面的代码是一些nextjs的基础代码,如react的runtime,以及用到的客户端组件的代码,不包含服务器组件的代码。会有一个内联的script脚本,用来表示这个服务器组件的JSX描述,用来给客户端的React 复用,直接利用该JSX描述生成fiber,避免重新生成该组件的fibe。客户端组件也会在服务端执行一次,只会使用该组件初次渲染的数据。比如useState的初始值。不会执行useEffect里面的effect函数,如果直接在组件函数内调用浏览器api会报错,建议将调用浏览器api的代码放入useEffect在客户端执行。
客户端组件无法渲染 RSC,当在客户端组件中import 引入RSC时,会自动将引入的组件转为 客户端组件,从而增加一些水合的性能开销。
客户端边界的组件父子关系并不重要。
use client
指令适用的是 文件/模块级别。即客户端组件中通过import
引入的组件才会变成客户端组件。如果是在 RSC中使用组合,组合中的父组件是 客户端组件,子组件并不会从 RSC变成 客户端组件。
// /components/ColorProvider.js
'use client';
import { DARK_COLORS, LIGHT_COLORS } from '@/constants.js';
import Footer from './Footer';
function ColorProvider({ children }) {
const [colorTheme, setColorTheme] = React.useState('light');
const colorVariables = colorTheme === 'light'
? LIGHT_COLORS
: DARK_COLORS;
return (
<body style={colorVariables}>
{children}
<Footer />
</body>
);
}
// /components/Homepage.js
import Header from './Header';
import MainContent from './MainContent';
import ColorProvider from './ColorProvider';
function Homepage() {
return (
<ColorProvider>
<Header />
<MainContent />
</ColorProvider>
);
}
如上代码,Homepage中import
的 Header, MainContent并不会因为是ColorProvider的子组件,就变成了客户端组件。 而Footer
组件,由于是在客户端组件中import
的,即使本身是RSC,这也会被转为客户端组件。
