
在react服务端渲染(ssr)应用中,服务器会预先生成html字符串并发送给客户端。客户端的react应用接收到这份html后,并不会从头开始构建dom,而是尝试“水合”已有的dom结构。水合过程是react将客户端javascript逻辑(如事件监听器、状态管理)附加到服务器预渲染的html上的过程。为了实现高效的水合,react要求服务器生成的html结构与客户端首次渲染时预期的dom结构完全一致。任何细微的差异,包括额外的文本节点(如换行符、空格),都可能导致水合失败,从而触发类似“expected server html to contain a matching 当使用Express和EJS等模板引擎进行React SSR时,一个常见且容易被忽视的问题是EJS模板中注入React组件的方式。考虑以下EJS模板片段: 在这个例子中,reactComponent变量包含了由renderToString生成的React组件的HTML字符串。表面上看,它被正确地放置在<div class="root">内部。然而,EJS模板中的换行符和注释(即使是HTML注释)在渲染时会被转换为实际的空白字符。这意味着,服务器最终发送给客户端的HTML可能看起来像这样: React的水合算法在检查<div class="root">的子节点时,会首先遇到一个文本节点(由换行和空格组成),而不是它所期望的<header>元素。这种不匹配就会立即触发水合警告,并导致React放弃水合,转而进行客户端渲染。 解决这个问题的关键在于消除EJS模板中reactComponent注入点周围的所有非预期空白。这意味着将reactComponent直接放置在根DOM元素的开始标签和结束标签之间,且不引入任何换行符或空格。 修改后的EJS模板示例: 通过这种方式,服务器生成的HTML将是: 现在,<div class="root">的第一个子节点就是React组件的根元素(例如<header>),与客户端React期望的结构完全匹配,从而实现成功的水合。 以下是一个完整的Express、EJS和React SSR设置示例,展示了如何正确处理组件注入: 1. React组件 (SchoolPage.jsx) 2. Express服务器 (server.js) 3. EJS模板 (school-page.ejs) 注意事项: id="root" vs class="root": 尽管将class="root"改为id="root"对解决此特定的水合警告并非决定性因素,但将React应用的根元素使用id="root"是普遍的最佳实践,因为它提供了唯一的标识符,方便客户端JavaScript精确地挂载React应用。 客户端水合: 确保客户端的JavaScript文件(例如school-page.js)在加载后会使用ReactDOM.hydrateRoot(或旧版ReactDOM.hydrate)来挂载React应用到id="root"元素上,例如: 其他水合不匹配: 除了EJS模板中的空白,其他因素也可能导致水合不匹配,例如: React SSR中的水合警告“Expected server HTML to contain a matching
常见问题:EJS模板中的隐形空白
<div class="root">
<!-- Rendered React component will be injected here -->
<%- reactComponent %>
</div>
<div class="root">
<!-- Rendered React component will be injected here -->
<header>...</header>
<section>...</section>
</div>
解决方案:确保组件的直接注入
<div class="root"><%- reactComponent %></div>
<div class="root"><header>...</header><section>...</section></div>
示例代码与实践
import React from 'react';
import MiniSearchBar from './MiniSearchBar'; // 假设有这个组件
import ContentContainer from './ContentContainer'; // 假设有这个组件
export default function SchoolPage() {
return (
<>
<header>
<picture title="Campus Eats">
<source
media="(min-width: 400px)"
srcSet="/images/campus-eats-logo-black.svg"
/>
<img src="/images/campus-eats-logo-mini.svg" alt="campus-eats-logo" />
</picture>
<nav className="places-at">
<h2>Places</h2>
<h2>at</h2>
<div className="search-container">
<MiniSearchBar></MiniSearchBar>
</div>
</nav>
<nav className="login-signup">
<button className="login">Log in</button>
<button className="signup">Sign up</button>
</nav>
</header>
<section>
<ContentContainer></ContentContainer>
</section>
</>
);
}import express from 'express';
import path from 'path';
import ejs from 'ejs';
import React from 'react';
import { renderToString } from 'react-dom/server';
import SchoolPage from './dist/SchoolPage.js'; // 假设打包后的组件
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, 'public'))); // 静态文件服务
app.get("/campus/:id/locations", async (req, res) => {
const reactComponentHtml = renderToString(<SchoolPage />);
const filePath = path.join(__dirname, "dist", "school-page.ejs"); // EJS模板路径
ejs.renderFile(filePath, { reactComponent: reactComponentHtml }, (err, html) => {
if (err) {
console.error("Error rendering template:", err);
return res.status(500).send("Internal Server Error");
}
res.send(html);
});
});
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 省略其他meta、link和script标签 -->
<title>Campus Eats</title>
</head>
<body>
<!-- 关键改动:确保 <%- reactComponent %> 无空白地直接嵌入 -->
<div id="root"><%- reactComponent %></div>
<script src="/school-page.js" defer></script> <!-- 客户端水合脚本 -->
</body>
</html>
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import SchoolPage from './SchoolPage'; // 客户端打包后的组件
const container = document.getElementById('root');
hydrateRoot(container, <SchoolPage />);
总结
以上就是解决React SSR水合警告:EJS模板中意外空白引发的DOM不匹配的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号