不是服务器端渲染,但仍然想提高 React 网站的性能?试试预渲染!
react-snap
是一个第三方库,可以将你网站上的页面预渲染为静态 HTML 文件。这可以提高你应用中的首次绘制时间。
这是一个比较:在模拟 3G 连接和移动设备上加载的,使用和不使用预渲染的同一个应用

为什么这很有用?
大型单页应用的主要性能问题是用户需要等待构成网站的 JavaScript 包完成下载,才能看到任何实际内容。包越大,用户等待的时间就越长。
为了解决这个问题,许多开发者采取在服务器上渲染应用的方法,而不是仅仅在浏览器中启动它。每次页面/路由转换时,完整的 HTML 都在服务器上生成并发送到浏览器,这虽然减少了首次绘制时间,但代价是首次字节到达时间 (Time to First Byte) 较慢。
预渲染 是一种独立的技术,它比服务器渲染复杂性更低,但也提供了一种提高你应用中首次绘制时间的方法。在构建时,使用无头浏览器(或没有用户界面的浏览器)来生成每个路由的静态 HTML 文件。这些文件随后可以与应用所需的 JavaScript 包一起发布。
react-snap
react-snap
使用 Puppeteer 来为你的应用中的不同路由创建预渲染的 HTML 文件。首先,将其作为开发依赖项安装
npm install --save-dev react-snap
然后在你的 package.json
文件中添加一个 postbuild
脚本
"scripts": {
//...
"postbuild": "react-snap"
}
每次应用程序进行新构建时(npm build
),这将自动运行 react-snap
命令。
你需要做的最后一件事是更改应用程序的启动方式。将 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 请求的 URL,然后单击 Chrome DevTools 中的 Previews 选项卡。
未设置样式的闪烁内容
虽然静态 HTML 现在几乎立即渲染,但默认情况下它仍然是未设置样式的,这可能会导致出现“未设置样式的闪烁内容” (FOUC) 问题。如果你使用 CSS-in-JS 库来生成选择器,这将尤其明显,因为 JavaScript 包必须完成执行后才能应用任何样式。
为了帮助防止这种情况,可以将关键 CSS(或初始页面渲染所需的最小 CSS 量)直接内联到 HTML 文档的 <head>
中。react-snap
在底层使用另一个第三方库 minimalcss
来提取不同路由的任何关键 CSS。你可以通过在你的 package.json
文件中指定以下内容来启用此功能
"reactSnap": {
"inlineCss": true
}
现在查看 Chrome DevTools 中的响应预览将显示内联了关键 CSS 的样式化页面。
结论
如果你没有在应用程序中进行服务器端渲染路由,请使用 react-snap
为你的用户预渲染静态 HTML。
- 将其作为开发依赖项安装,并从默认设置开始。
- 如果实验性的
inlineCss
选项适用于你的网站,请使用它来内联关键 CSS。 - 如果你在任何路由内的组件级别上使用代码拆分,请注意不要向用户预渲染加载状态。
react-snap
README 更详细地介绍了这一点。