服务端渲染

服务器端渲染 (SSR) 是最古老的 Web 内容渲染方法之一。

SSR 为要呈现的页面内容生成完整的 HTML,以响应用户请求。

内容可能包括来自数据存储或外部 API 的数据。

连接和获取操作在服务器上处理。
格式化内容所需的 HTML 也会在服务器上生成。

因此,使用 SSR,可以避免为数据获取和模板制作额外的往返行程。

因此,客户端不需要渲染代码,并且不需要将与此对应的 JavaScript 发送到客户端。

使用 SSR,每个请求都被独立处理,并将被服务器作为新请求处理。

即使连续两次请求的输出差别不大,服务器也会从头开始处理生成。

由于服务器是多个用户共用的,因此处理能力在给定时间由所有活动用户共享。


经典 SSR 实现

让我们看看如何使用经典的 SSR 和 JavaScript 创建一个页面来显示当前时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//index.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>

//index.js
function tick() {
    var d = new Date();
    var n = d.toLocaleTimeString();
    document.getElementById("currentTime").innerHTML = n;
}
setInterval(tick, 1000);

请注意这与提供相同输出的 CSR 代码有何不同。

另请注意,虽然 HTML 由服务器呈现,但此处显示的时间是由 JavaScript 函数 tick() 填充的客户端本地时间。

如果要显示服务器特定的任何其他数据,例如服务器时间,则需要在呈现之前将其嵌入 HTML 中。

这意味着它不会在没有往返服务器的情况下自动刷新。


优缺点

在服务器上执行渲染代码并减少 JavaScript 具有以下优点。

较少的 JavaScript 导致更快的 FCP 和 TTI

在页面上有多个 UI 元素和应用程序逻辑的情况下,与 CSR 相比,SSR 的 JavaScript 少得多。

因此加载和处理脚本所需的时间更少。

FP、FCP 和 TTI 较短且 FCP = TTI。

使用 SSR,用户将不会等待所有屏幕元素出现并使其具有交互性。

为客户端 JavaScript 提供额外预算

开发团队需要使用 JS 预算来限制页面上的 JS 数量,以实现所需的性能。

使用 SSR,由于直接消除了呈现页面所需的 JS,因此它为应用程序可能需要的任何第三方 JS 创造了额外的空间。

启用搜索引擎优化

搜索引擎爬虫可以轻松抓取 SSR 应用程序的内容,从而确保页面上的搜索引擎优化更高。

由于上述优点,SSR 非常适合静态内容。 但是,它确实有一些缺点,因此它并不适合所有场景。

慢速 TTFB

由于所有处理都在服务器上进行,在以下一种或多种情况下,服务器的响应可能会延迟

  • 多个并发用户导致服务器负载过大

  • 网速慢

  • 服务器代码未优化

某些交互需要重新加载整页

由于客户端上的所有代码都不可用,因此所有导致整个页面重新加载的关键操作都需要频繁地往返于服务器。

这可能会增加交互之间的时间,因为用户需要在操作之间等待更长时间。 因此,SSR 无法实现单页应用程序。

为了解决这些缺点,现代框架和库允许在服务器和客户端上为同一应用程序进行渲染。

我们将在以下部分详细介绍这些内容。

首先,让我们看看 Next.js 的一种更简单的 SSR 形式。


使用 Next.js 的 SSR

Next.js 框架也支持 SSR。

这会在每次请求时在服务器上预渲染一个页面。

它可以通过从页面导出一个名为 getServerSideProps() 的异步函数来完成,如下所示。

1
2
3
4
5
6

export async function getServerSideProps(context) {
  return {
    props: {}, // 将作为道具传递给页面组件
  }
}

上下文对象包含 HTTP 请求和响应对象、路由参数、查询字符串、区域设置等的键。

以下实现显示了使用 getServerSideProps() 在使用 React 格式化的页面上呈现数据。

完整的实现在这里

服务器的 React

React 可以同构渲染,这意味着它既可以在浏览器上运行,也可以在服务器等其他平台上运行。
因此,可以使用 React 在服务器上呈现 UI 元素。

React 还可以与通用代码一起使用,这将允许相同的代码在多个环境中运行。
这是通过在服务器上使用 Node.js 或所谓的 Node 服务器来实现的。
因此,通用 JavaScript 可用于在服务器上获取数据,然后使用同构 React 进行渲染。

让我们看看使这成为可能的 React 函数。

ReactDOMServer.renderToString(element)

这个函数返回一个对应于 React 元素的 HTML 字符串。
然后可以将 HTML 呈现给客户端以加快页面加载速度。

renderToString() 函数可以与 ReactDOM.hydrate() 一起使用。
这将确保呈现的 HTML 原样保留在客户端上,并且仅在加载后附加事件处理程序。

为了实现这一点,我们在客户端和服务器上使用与每个页面对应的 .js 文件。
服务器上的 .js 文件将呈现 HTML 内容,客户端上的 .js 文件将其水合

假设您有一个名为 App 的 React 元素,其中包含在通用 app.js 文件中定义的要呈现的 HTML。
服务器端和客户端 React 都可以识别 App 元素。

服务器上的 ipage.js 文件可以有以下代码:

app.get('/', (req, res) => {
  const app = ReactDOMServer.renderToString(<App />);
})

现在可以使用常量 App 来生成要呈现的 HTML。

客户端的 ipage.js 将有以下内容,以确保元素 App 是水合的。

ReactDOM.hydrate(<App />, document.getElementById('root'));

可以在此处找到带有 React 的 SSR 的完整示例


知识点

  • FP
  • FCP
  • TTI
  • SEO
  • 同构渲染 ReactDOMServer.renderToString(element)
  • 通用 JavaScript
  • ReactDOM.hydrate()
  • 水合