server-side-rendering
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseServer-side Rendering
服务器端渲染(SSR)
Server-side rendering (SSR) is one of the oldest methods of rendering web content. SSR generates the full HTML for the page content to be rendered in response to a user request. The content may include data from a datastore or external API.
The connect and fetch operations are handled on the server. HTML required to format the content is also generated on the server. Thus, with SSR we can avoid making additional round trips for data fetching and templating. As such, rendering code is not required on the client and the JavaScript corresponding to this need not be sent to the client.
服务器端渲染(SSR)是最古老的网页内容渲染方式之一。SSR会根据用户请求生成待渲染页面内容的完整HTML。这些内容可能包含来自数据存储或外部API的数据。
连接和数据获取操作在服务器端处理。用于格式化内容所需的HTML也在服务器端生成。因此,使用SSR可以避免为数据获取和模板渲染进行额外的往返请求。这样一来,客户端就不需要渲染代码,也无需发送对应的JavaScript到客户端。
When to Use
适用场景
- Use this when SEO and fast First Contentful Paint are important for your application
- This is helpful for content-heavy pages that need to be quickly visible to users and search engines
- 当SEO和快速的首次内容绘制(First Contentful Paint)对您的应用至关重要时,请使用此方式
- 这对于需要快速让用户和搜索引擎可见的内容密集型页面很有帮助
Instructions
实施建议
- Use frameworks like Next.js that provide built-in SSR support
- Consider upgrading to (React 18+) for streaming SSR with Suspense support
renderToPipeableStream - Combine SSR with client-side hydration for interactive pages
- Be aware of TTFB implications — optimize server response times and consider caching
- Explore React Server Components as a complement to SSR for reducing client-side JavaScript
- 使用Next.js等内置SSR支持的框架
- 考虑升级到(React 18+)以支持带有Suspense的流式SSR
renderToPipeableStream - 将SSR与客户端水合(hydration)结合用于交互式页面
- 注意TTFB的影响——优化服务器响应时间并考虑缓存
- 探索React Server Components作为SSR的补充,以减少客户端JavaScript
Details
详细说明
With SSR every request is treated independently and will be processed as a new request by the server. Even if the output of two consecutive requests is not very different, the server will process and generate it from scratch. Since the server is common to multiple users, the processing capability is shared by all active users at a given time.
在SSR中,每个请求都被独立处理,服务器会将其作为新请求进行处理。即使连续两个请求的输出差异不大,服务器也会从头开始处理并生成内容。由于服务器为多个用户共享,其处理能力会被特定时间点的所有活跃用户分摊。
Classic SSR Implementation
传统SSR实现示例
Consider a simple example showing and updating the current time on a page using classic SSR and JavaScript.
html
<!DOCTYPE html>
<html>
<head>
<title>Time</title>
</head>
<body>
<div>
<h1>Hello, world!</h1>
<b>It is <div id=currentTime></div></b>
</div>
</body>
</html>js
function tick() {
var d = new Date();
var n = d.toLocaleTimeString();
document.getElementById("currentTime").innerHTML = n;
}
setInterval(tick, 1000);Note how this is different from the CSR code that provides the same output. Also note that, while the HTML is rendered by the server, the time displayed here is the local time on the client as populated by the JavaScript function . If you want to display any other data that is server specific, e.g., server time, you will need to embed it in the HTML before it is rendered. This means it will not get refreshed automatically without a round trip to the server.
tick()考虑一个简单的示例,展示如何使用传统SSR和JavaScript在页面上显示并更新当前时间。
html
<!DOCTYPE html>
<html>
<head>
<title>Time</title>
</head>
<body>
<div>
<h1>Hello, world!</h1>
<b>It is <div id=currentTime></div></b>
</div>
</body>
</html>js
function tick() {
var d = new Date();
var n = d.toLocaleTimeString();
document.getElementById("currentTime").innerHTML = n;
}
setInterval(tick, 1000);请注意,这与实现相同输出的CSR代码有何不同。另外需要注意的是,虽然HTML由服务器渲染,但此处显示的时间是客户端的本地时间,由JavaScript函数填充。如果您想显示任何服务器特定的数据(例如服务器时间),则需要在HTML渲染前将其嵌入其中。这意味着如果不向服务器发起往返请求,数据将无法自动刷新。
tick()Pros and Cons
优缺点
Executing the rendering code on the server and reducing JavaScript offers the following advantages.
在服务器端执行渲染代码并减少JavaScript带来以下优势。
Lesser JavaScript leads to quicker FCP and TTI
更少的JavaScript实现更快的FCP和TTI
In cases where there are multiple UI elements and application logic on the page, SSR has considerably less JavaScript when compared to CSR. The time required to load and process the script is thus lesser. FP, FCP and TTI are shorter and FCP = TTI. With SSR, users will not be left waiting for all the screen elements to appear and for it to become interactive.
当页面上有多个UI元素和应用逻辑时,与CSR相比,SSR所需的JavaScript要少得多。加载和处理脚本的时间因此更短。FP(首次绘制)、FCP(首次内容绘制)和TTI(可交互时间)都会缩短,且FCP等于TTI。使用SSR,用户无需等待所有屏幕元素加载完成并变为可交互状态。
Provides additional budget for client-side JavaScript
为客户端JavaScript预留额外预算
Development teams are required to work with a JS budget that limits the amount of JS on the page to achieve the desired performance. With SSR, since you are directly eliminating the JS required to render the page, it creates additional space for any third party JS that may be required by the application.
开发团队需要在JavaScript预算内工作,限制页面上的JavaScript数量以达到预期性能。使用SSR时,由于直接消除了页面渲染所需的JavaScript,这为应用可能需要的任何第三方JavaScript腾出了空间。
SEO enabled
支持SEO优化
Search engine crawlers are easily able to crawl the content of an SSR application thus ensuring higher search engine optimization on the page.
Note (React 18+): Adopt Streaming SSR with SuspenseTraditional SSR renders the full HTML on the server for each request. React 18 introduced Streaming Server-Side Rendering, which sends HTML to the client in chunks as it's generated, rather than waiting for the whole render to finish. This significantly improves TTFB and LCP—users see the page shell almost immediately.If your SSR implementation uses older APIs likeorReactDOMServer.renderToString, consider upgrading torenderToNodeStream(Node) orrenderToPipeableStream(for edge environments) introduced in React 18. These APIs support Suspense boundaries, allowing you to send partial content and display arenderToReadableStreamfor slow parts.<Suspense fallback>React Server Components: Perhaps the biggest change is the rise of React Server Components (RSC). RSCs allow you to render part of your UI on the server ahead of time without sending the associated JS to the client, dramatically shrinking client JS bundles.
SSR works great for static content due to the above advantages. However, it does have a few disadvantages because of which it is not perfect for all scenarios.
搜索引擎爬虫可以轻松抓取SSR应用的内容,从而确保页面获得更高的搜索引擎优化效果。
注意(React 18+):采用带Suspense的流式SSR传统SSR会为每个请求在服务器端渲染完整的HTML。React 18引入了流式服务器端渲染,它会在生成HTML时将其分块发送给客户端,而不是等待整个渲染完成。这显著改善了TTFB(首字节时间)和LCP(最大内容绘制)——用户几乎可以立即看到页面框架。如果您的SSR实现使用较旧的API(如或ReactDOMServer.renderToString),请考虑升级到React 18中引入的renderToNodeStream(适用于Node环境)或renderToPipeableStream(适用于边缘环境)。这些API支持Suspense边界,允许您发送部分内容,并为加载缓慢的部分显示renderToReadableStream。<Suspense fallback>React Server Components: 最大的变化可能是**React Server Components(RSC)**的兴起。RSC允许您提前在服务器端渲染部分UI,而无需将相关的JavaScript发送到客户端,从而大幅缩小客户端JavaScript包的体积。
由于上述优势,SSR非常适合静态内容。然而,它也存在一些缺点,因此并非适用于所有场景。
Slow TTFB
TTFB较慢
Since all processing takes place on the server, the response from the server may be delayed in case of one or more of the following scenarios:
- Multiple simultaneous users causing excess load on the server.
- Slow network
- Server code not optimized.
由于所有处理都在服务器端进行,在以下一种或多种情况下,服务器响应可能会延迟:
- 多个用户同时访问导致服务器负载过高。
- 网络速度缓慢
- 服务器代码未优化
Full page reloads required for some interactions
部分交互需要整页刷新
Since all code is not available on the client, frequent round trips to the server are required for all key operations causing full page reloads. This could increase the time between interactions as users are required to wait longer between operations. A single-page application is thus not possible with SSR.
由于客户端没有完整的代码,所有关键操作都需要频繁向服务器发起往返请求,导致整页刷新。这可能会增加交互之间的时间,因为用户需要在操作之间等待更长时间。因此,SSR无法实现单页应用(SPA)。
SSR with Next.js
使用Next.js实现SSR
The Next.js framework also supports SSR. This pre-renders a page on the server on every request. It can be accomplished by exporting an async function called from a page as follows.
getServerSideProps()js
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
};
}The context object contains keys for HTTP request and response objects, routing parameters, querystring, locale, etc.
The following implementation shows the use of for rendering data on a page formatted using React:
getServerSideProps()js
// data fetched from an external data source using `getServerSideProps`
const Users = ({ users, error }) => {
return (
<section>
<header>
<h1>List of users</h1>
</header>
{error && <div>There was an error.</div>}
{!error && users && (
<table>
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{users.map((user, key) => (
<tr key={key}>
<td>{user.username}</td>
<td>{user.email}</td>
<td>{user.name}</td>
</tr>
))}
</tbody>
</table>
)}
</section>
);
};
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch("https://jsonplaceholder.typicode.com/users")
const data = await res.json();
// Pass data to the page via props
return { props: { data } }
}
export default Users;Next.js框架也支持SSR。它会在每次请求时在服务器端预渲染页面。可以通过从页面导出一个名为的异步函数来实现,如下所示。
getServerSideProps()js
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
};
}context对象包含HTTP请求和响应对象、路由参数、查询字符串、区域设置等键值对。
以下实现展示了如何使用在React格式化的页面上渲染数据:
getServerSideProps()js
// data fetched from an external data source using `getServerSideProps`
const Users = ({ users, error }) => {
return (
<section>
<header>
<h1>List of users</h1>
</header>
{error && <div>There was an error.</div>}
{!error && users && (
<table>
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{users.map((user, key) => (
<tr key={key}>
<td>{user.username}</td>
<td>{user.email}</td>
<td>{user.name}</td>
</tr>
))}
</tbody>
</table>
)}
</section>
);
};
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch("https://jsonplaceholder.typicode.com/users")
const data = await res.json();
// Pass data to the page via props
return { props: { data } }
}
export default Users;React for the Server
服务端React
React can be rendered isomorphically, which means that it can function both on the browser as well as other platforms like the server. Thus, UI elements may be rendered on the server using React.
React can also be used with universal code which will allow the same code to run in multiple environments. This is made possible by using Node.js on the server.
js
ReactDOMServer.renderToString(element);This function returns an HTML string corresponding to the React element. The HTML can then be rendered to the client for a faster page load.
The function may be used with . This preserves the HTML rendered on the server and attaches event handlers on the client.
renderToString()hydrateRoot()To implement this, we use a file on both client and server corresponding to every page. The file on the server will render the HTML content, and the file on the client will hydrate it.
.js.js.jsThe server code:
js
app.get("/", (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
});The client-side code to ensure the element is hydrated:
Appjs
import { hydrateRoot } from "react-dom/client";
hydrateRoot(document.getElementById("root"), <App />);A complete example of SSR with React can be found here.
React可以进行同构渲染,这意味着它既可以在浏览器中运行,也可以在服务器等其他平台上运行。因此,可以使用React在服务器端渲染UI元素。
React还可以与通用代码一起使用,允许相同的代码在多个环境中运行。这通过在服务器端使用Node.js实现。
js
ReactDOMServer.renderToString(element);此函数返回与React元素对应的HTML字符串。然后可以将该HTML渲染到客户端以实现更快的页面加载。
renderToString()hydrateRoot()要实现这一点,我们为每个页面在客户端和服务器端分别使用一个文件。服务器端的文件将渲染HTML内容,客户端的文件将对其进行水合。
.js.js.js服务器代码:
js
app.get("/", (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
});客户端代码,确保元素被水合:
Appjs
import { hydrateRoot } from "react-dom/client";
hydrateRoot(document.getElementById("root"), <App />);完整的React SSR示例可以在这里找到。