使用回应贴靠预渲染路线

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

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

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

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

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

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

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

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

react-snap

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 载荷的显示效果,请点击 HTML 请求的网址,然后点击 Chrome 开发者工具中的预览标签页。

对比效果。后面的画面显示内容已呈现。

未设置样式的内容闪烁

虽然静态 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 对此进行了详细介绍。