使用回应贴靠预渲染路线

您的网站不是服务器端渲染,但仍希望加快其 React 网站的性能?试试预渲染!

react-snap 是一个第三方库,可将您网站上的网页预渲染为静态 HTML 文件。这可以缩短应用中的首次绘制时间。

以下是同一应用在模拟的 3G 连接和移动设备上加载预渲染内容与未加载预渲染内容的对比:

并排加载对比。使用预渲染的版本的加载速度提高了 4.2 秒。

为什么搜索渠道报告非常实用?

大型单页应用的主要性能问题是,用户需要等待构成网站的 JavaScript 软件包下载完成后,才能看到实际内容。软件包越大,用户需要等待的时间就越长。

为了解决此问题,许多开发者采用在服务器上呈现应用的方法,而不是仅在浏览器中启动应用。每次页面/路由转换时,都会在服务器上生成完整的 HTML 并将其发送到浏览器,这样可以缩短首次绘制时间,但代价是至第一字节的时间变长。

预渲染是一种比服务器渲染不太复杂的单独技术,但也提供了一种缩短应用的首次绘制时间的方法。无头浏览器(即没有界面的浏览器)用于在构建时生成每个路由的静态 HTML 文件。然后,这些文件可与应用所需的 JavaScript 软件包一起提供。

回应截取

react-snap 使用 Puppeteer 在应用中创建不同路由的预渲染 HTML 文件。首先,将其作为开发依赖项进行安装:

npm install --save-dev react-snap

然后,在 package.json 中添加 postbuild 脚本:

"scripts": {
  //...
  "postbuild": "react-snap"
}

这样,系统就会在每次构建应用的新 build 时自动运行 react-snap 命令 (npm build)。

您需要执行的最后一项操作是更改应用的启动方式。将 src/index.js 文件更改为以下内容:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
const rootElement = document.getElementById("root");

if (rootElement.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

此方法不仅会使用 ReactDOM.render 将根 React 元素直接呈现到 DOM,还会检查是否已存在任何子节点,以确定 HTML 内容是否已预渲染(或在服务器上呈现)。在这种情况下,系统将改用 ReactDOM.hydrate 将事件监听器附加到已创建的 HTML 上,而不是创建新的 HTML。

现在,构建应用将为每个抓取的路由生成静态 HTML 文件作为载荷。您可以点击 HTML 请求的网址,然后点击 Chrome 开发者工具中的 Previews 标签页,以查看 HTML 载荷的外观。

前后对比分析。后面的画面显示内容已呈现。

闪烁无样式的内容

虽然静态 HTML 现在几乎会立即呈现,但默认情况下仍未设置样式,这可能会导致显示“未设置样式的闪烁内容”(FOUC) 问题。如果您使用 CSS-in-JS 库生成选择器,这一点尤为明显,因为 JavaScript 软件包必须先完成执行,然后才能应用任何样式。

为帮助防止这种情况,关键 CSS(即初始页面渲染所需的最低 CSS 量)可以直接内嵌到 HTML 文档的 <head> 中。react-snap 会在后台使用另一个第三方库 minimalcss 来提取适用于不同路线的任何关键 CSS。您可以在 package.json 文件中指定以下内容来启用此功能:

"reactSnap": {
  "inlineCss": true
}

现在,查看 Chrome 开发者工具中的响应预览,您会看到内嵌了关键 CSS 的样式网页。

前后对比分析。后面的画面显示,由于内嵌了关键 CSS,内容已呈现并设置了样式。

总结

如果您未在应用中进行服务器端渲染路由,请使用 react-snap 向用户预呈现静态 HTML。

  1. 将其作为开发依赖项进行安装,并仅使用默认设置开始构建。
  2. 如果实验性 inlineCss 选项适用于您的网站,请使用该选项内嵌关键 CSS。
  3. 如果您在任何路线中在组件级别使用代码分块,请务必不要向用户预渲染加载状态。react-snap README 对此进行了详细介绍。