You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React服务端渲染报错:Invariant Violation: Invalid tag: <!doctype html> 如何解决?

解决React SSR中的两个问题:Invalid Tag错误与数据传递失败

先来说你遇到的第一个错误——Invariant Violation: Invalid tag: <!doctype html>,这个问题其实很常见,根源是你误解了ReactDOMServer.renderToString的作用:它只能用来渲染React元素,而<!doctype html>是HTML文档的声明,不属于React能识别的组件/标签范畴。

修复Invalid Tag错误

之前你可能尝试直接把整个HTML文档字符串传给renderToString,这肯定会报错。正确的做法是拆分两步:

  1. 用renderToString单独渲染你的根组件(比如<App />),得到组件对应的HTML字符串。
  2. 把这个字符串插入到预先写好的完整HTML模板里(包含doctype、head、body等结构)。

举个具体的代码示例:

// 服务端代码
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';

// 1. 渲染React根组件
const appHtml = ReactDOMServer.renderToString(<App />);

// 2. 拼接完整的HTML文档
const fullHtml = `<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Your App</title>
  </head>
  <body>
    <div id="root">${appHtml}</div>
    <!-- 别忘了引入客户端React脚本,用于hydrate激活交互 -->
    <script src="/client-bundle.js"></script>
  </body>
</html>`;

// 发送给浏览器
res.send(fullHtml);

这样就不会触发Invalid Tag的错误了,因为React只负责渲染你的App组件,剩下的HTML结构由你自己拼接。


解决数据未传递到App的问题

这个问题通常和SSR场景下的服务端-客户端数据同步有关,下面是几个排查和修复的关键点:

1. 确保服务端正确传递props给App

首先检查服务端渲染<App />时,是否真的把数据作为props传进去了。比如:

// 服务端:假设你有要传递的初始数据
const initialData = { posts: [...], user: {...} };

// 一定要把数据作为props传给App
const appHtml = ReactDOMServer.renderToString(<App initialData={initialData} />);

可以在服务端用console.log(initialData)确认数据是否存在,有没有为空或者未定义的情况。

2. 同步客户端与服务端的初始数据

SSR的核心是服务端先渲染出带数据的HTML,客户端再"hydrate"(激活)这个HTML,让它变成可交互的React应用。如果客户端的App没有拿到和服务端一样的初始数据,就会出现数据不显示或者页面闪烁的问题。

解决方法是把服务端的初始数据挂载到全局window对象上,让客户端可以读取:

// 服务端拼接HTML时,添加这行代码
const fullHtml = `<!doctype html>
<html>
  <!-- ... 其他结构 ... -->
  <body>
    <div id="root">${appHtml}</div>
    <!-- 把初始数据注入到全局,注意要转义避免XSS -->
    <script>window.__INITIAL_DATA__ = ${JSON.stringify(initialData).replace(/</g, '\\u003c')}</script>
    <script src="/client-bundle.js"></script>
  </body>
</html>`;

然后在客户端hydrate时,把这个全局数据传给App:

// 客户端代码
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.hydrate(
  <App initialData={window.__INITIAL_DATA__} />,
  document.getElementById('root')
);

这样服务端和客户端的App用的是同一套初始数据,就能保证渲染结果一致。

3. 检查App组件是否正确接收props

最后,确认你的App组件确实在使用传入的props。比如用函数组件的话:

function App({ initialData }) {
  // 可以在这里打印props,确认是否接收到数据
  console.log('Initial data received:', initialData);
  
  return (
    <div className="app-container">
      {/* 使用initialData渲染内容 */}
      {initialData?.posts?.map(post => (
        <div key={post.id} className="post-item">
          <h3>{post.title}</h3>
          <p>{post.content}</p>
        </div>
      ))}
    </div>
  );
}

如果是类组件,要通过this.props.initialData来访问。


按照上面的步骤,应该能同时解决这两个问题。如果还有疑问,可以检查一下是否有拼写错误(比如props名称写错),或者服务端数据是否在序列化时出了问题(比如JSON.stringify不能处理循环引用的数据)。

内容的提问来源于stack exchange,提问作者Nikita Vlasenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:07:48