React 18 服务器组件深度解析
React 服务器组件 (RSC) 是一项极具潜力的功能,它能够显著提升页面加载性能、减小包大小,并革新我们编写 React 应用的方式。虽然这项功能在 React 18 中仍处于实验阶段,但了解其底层工作原理仍然十分重要。
服务器和客户端组件的分离
React 服务器组件允许服务器和客户端(浏览器)协同工作来渲染你的 React 应用。React 元素树中的一些组件由服务器渲染,另一些则由浏览器渲染。这与服务器端渲染 (SSR) 不同。SSR 模拟了一个将 React 树渲染成原始 HTML 的环境,但它并不区分服务器组件和客户端组件。
React 团队根据组件所在文件的扩展名定义了服务器端组件和客户端组件:如果文件以 .server.jsx 结尾,则包含服务器端组件;如果以 .client.jsx 结尾,则包含客户端组件;如果既不以 .server.jsx 结尾也不以 .client.jsx 结尾,则包含既可用作服务器端组件也可用作客户端组件的组件。
RSC渲染器的生活
使用 RSC 的页面生命周期始终始于服务器端,服务器端会响应 API 调用来渲染 React 组件。然后,服务器会将根组件元素序列化为 JSON。最终目标是将初始的根服务器组件渲染成一个包含基本 HTML 标签和客户端组件占位符的树状结构。
然后服务器将此序列化的树发送到浏览器,浏览器可以对其进行反序列化,用实际的客户端组件填充客户端占位符,并渲染最终结果。
RSC和悬疑
Suspense 在 React 组件架构 (RSC) 中扮演着重要角色。Suspense 允许你在 React 组件需要一些尚未准备就绪的内容(例如获取数据、延迟导入组件等)时抛出 Promise。这些 Promise 会在 Suspense 边界处被捕获。当渲染 Suspense 子树时抛出 Promise,React 会暂停渲染该子树,直到 Promise 被解析,然后才会再次尝试渲染。
RSC线格式
服务器输出的格式很简单,每行包含一个带有 ID 的 JSON 数据块。这种格式非常适合流式传输——客户端读取完一行后,就可以解析 JSON 片段并继续执行后续操作。
使用 RSC 格式
该react-server-dom-webpack软件包包含接收 RSC 响应并重建 React 元素树的入口点。服务器完成数据加载后,会输出模块引用行(该引用定义了组件的模块引用)以及应该替换到引用所在位置的 React 元素树。
RSC 与从客户端组件获取数据
RSC 是否优于从客户端组件获取数据,取决于你要渲染到屏幕上的内容。使用 RSC,你可以获得非规范化的“处理后”数据,这些数据可以直接映射到你向用户显示的内容。如果渲染需要多次数据获取,且这些获取操作呈瀑布式顺序,那么最好在服务器端进行数据获取,因为服务器端的数据延迟要低得多,而不是在浏览器端。
RSC 和 SSR
使用 React 18,可以将 SSR 和 RSC 结合起来,因此可以在服务器端生成 HTML,然后在浏览器中使用 RSC 来加载该 HTML。
服务器端渲染(SSR)
服务器端渲染 (SSR) 是一种将 React 应用程序在服务器端渲染成静态 HTML 字符串,然后将其发送给客户端的技术。这可以通过允许在所有 JavaScript 加载和解析完成之前显示页面来提高性能和 SEO。
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';
const server = express();
server.get('/', (req, res) => {
const appString = ReactDOMServer.renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="root">${appString}</div>
<script src="/bundle.js"></script>
</body>
</html>
`);
});
server.listen(8080);
在这个例子中,App 组件在服务器端被渲染成一个字符串,然后该字符串被插入到 HTML 响应中。客户端随后将这个静态 HTML “加载”成一个完全交互式的 React 应用。
React 服务器组件 (RSC)
React 服务器组件 (RSC) 允许服务器和客户端协同工作来渲染 React 应用程序。一些组件在服务器端渲染,另一些则在客户端渲染。这可以通过减少发送到客户端的 JavaScript 代码量并允许服务器直接获取和渲染数据来提高性能。
以下是一个简单的RSC示例:
// Message.server.js
import {db} from './db.server';
function Message({id}) {
const message = db.messages.get(id);
return (
<div>
<h1>{message.title}</h1>
<p>{message.body}</p>
</div>
);
}
export default Message;
// App.client.js
import {useState} from 'react';
import Message from './Message.server';
function App() {
const [selectedId, setSelectedId] = useState(1);
return (
<div>
<button onClick={() => setSelectedId(selectedId + 1)}>
Next
</button>
<Message id={selectedId} />
</div>
);
}
export default App;
在这个例子中,消息服务器组件根据 id 属性检索并渲染一条消息。应用程序客户端组件维护一个状态(selectedId),并使用当前 id 渲染消息服务器组件。
实用建议
-
了解服务器组件和客户端组件之间的区别:了解哪些组件由服务器渲染,哪些组件由客户端渲染,对于有效使用 RSC 至关重要。
-
将 Suspense 与 RSC 结合使用: Suspense 允许你在 React 组件中处理 Promise,这对于 RSC 的功能至关重要。
-
优化性能: RSC 可以显著提升页面加载速度并减小文件包大小。但是,务必衡量并监控这些指标,以确保获得预期效果。
-
权衡利弊:虽然 RSC 提供了诸多优势,但也存在一些不足。例如,服务器组件无法使用状态或效果,这可能会限制它们在某些情况下的使用。
-
在应用程序的非关键部分试用 RSC:鉴于 RSC 的实验性质,最好先在应用程序的非关键部分进行试用。这将有助于您更好地了解其工作原理以及如何有效地使用它。
-
将 RSC 与其他 React 功能结合使用: RSC 可以与其他 React 功能(如 Suspense 和并发模式)结合使用,以构建更高效、更用户友好的应用程序。
例如。
让我们来看一些代码示例,以说明 React 服务器组件 (RSC) 的工作原理。
首先,我们来定义一个服务器组件。服务器组件通过 .server.js 扩展名来识别:
// Message.server.js
import {db} from './db.server';
function Message({id}) {
const message = db.messages.get(id);
return (
<div>
<h1>{message.title}</h1>
<p>{message.body}</p>
</div>
);
}
export default Message;
在这个例子中,Message 是一个服务器组件,它根据 id 属性从数据库中获取消息。请注意,我们直接导入了一个服务器模块 (db.server) 并使用它来获取数据。在客户端组件中是不能这样做的。
接下来,我们定义一个使用此服务器组件的客户端组件:
// App.client.js
import {useState} from 'react';
import Message from './Message.server';
function App() {
const [selectedId, setSelectedId] = useState(1);
return (
<div>
<button onClick={() => setSelectedId(selectedId + 1)}>
Next
</button>
<Message id={selectedId} />
</div>
);
}
export default App;
在这个例子中,App 是一个客户端组件,它维护着一个状态(selectedId),并使用当前 ID 渲染 Message 服务器组件。当点击“下一步”按钮时,selectedId 的值会递增,从而导致 Message 组件使用新的 ID 再次渲染。
这是一个简单的例子,但它说明了 RSC 背后的关键思想:服务器组件可用于在服务器上获取和渲染数据,而客户端组件则在客户端保持交互性。
比较
虽然 SSR 和 RSC 都涉及服务器端渲染,但它们的用途不同,优缺点也不同:
服务端渲染(SSR) 主要通过向客户端发送静态 HTML 来提升性能和 SEO。然而,它需要将所有 JavaScript 代码都发送到客户端,这会导致代码量庞大且解析速度缓慢。
另一方面,响应式渲染 (RSC) 允许服务器和客户端协同渲染,从而减少发送到客户端的 JavaScript 代码量并提升性能。但是,服务器组件无法使用状态或副作用,这可能会限制其在某些场景下的使用。
总而言之,SSR 和 RSC 都是强大的 React 应用渲染技术,各有优缺点。了解这些差异可以帮助您在项目中更明智地选择合适的技术。
相关链接:
https://react.dev/blog/2022/03/29/react-v18
https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components
https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components
https://shopify.github.io/hydrogen-v1/tutorials/react-server-components
https://www.plasmic.app/blog/how-react-server-components-work
