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

React Static Router服务端渲染报错:Unexpected token '<'求助

问题:React SSR结合Express启动时出现SyntaxError: Unexpected token '<'

启动服务器时出现如下错误:

<StaticRouter location={req.url}>
    ^

SyntaxError: Unexpected token '<'
    at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:118:18)
    at callTranslator (node:internal/modules/esm/loader:273:14)
    at ModuleLoader.moduleProvider (node:internal/modules/esm/loader:278:30)
    at async link (node:internal/modules/esm/module_job:76:21)

相关代码:

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import "./index.css";
import "bootstrap/dist/css/bootstrap.min.css";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

app.jsx

import "./App.css";
import { Routes, Route } from "react-router-dom";
import Navi from "./components/Nav";
import Homepage from "./components/home";
import SignIn from "./components/signin";
import SignUp from "./components/signup";
import ErrorPage from "./components/error";

function App() {
  return (
    <html>
      <title>Meetings App</title>
      <body>
        <div id="app">
          <Routes>
            <Route path="/" element={<Navi />}>
              <Route index element={<Homepage />}></Route>
              <Route path="/signup" element={<SignUp />}></Route>
              <Route path="/signin" element={<SignIn />}></Route>
            </Route>
            <Route path="*" element={<ErrorPage />}></Route>
          </Routes>
        </div>
        <script src="./index.js" />
      </body>
    </html>
  );
}

export default App;

server.jsx

import express from "express";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom/server";
import App from "../src/App";
const http = require("http");

const app = express();
const server = http.createServer(app);

app.get("/", (req, res) => {
  let html = ReactDOMServer.renderToString(
    <StaticRouter location={req.url}>
      <App />
    </StaticRouter>
  );

  res.send("<!DOCTYPE html>" + html);
});

let PORT = process.env.PORT || 5000;
server.listen(PORT, () => {
  console.log(`server running on port ${PORT}`);
});

解决方案

1. 核心问题:Node.js无法直接解析JSX

Node.js默认只能处理纯JavaScript,你的server.jsx中直接使用了JSX语法(<StaticRouter>标签),Node尝试执行时无法识别<符号,导致语法错误。需要用Babel编译服务器端代码。

操作步骤:

  • 安装Babel依赖:
    npm install @babel/core @babel/node @babel/preset-env @babel/preset-react --save-dev
    
  • 在项目根目录创建.babelrc文件:
    {
      "presets": ["@babel/preset-env", "@babel/preset-react"]
    }
    
  • 修改package.json中的启动命令,用babel-node运行服务器文件:
    "scripts": {
      "start:server": "babel-node path/to/server.jsx"
    }
    

2. 修复App组件的结构问题

你的App组件直接返回<html>和<body>标签,会导致SSR渲染后生成嵌套的html结构(服务器已输出<!DOCTYPE html>,App里又返回<html>)。正确做法是:

  • App组件只包含应用内容部分,把html、body、title等放在服务器端的模板中。

修改后的app.jsx:

import "./App.css";
import { Routes, Route } from "react-router-dom";
import Navi from "./components/Nav";
import Homepage from "./components/home";
import SignIn from "./components/signin";
import SignUp from "./components/signup";
import ErrorPage from "./components/error";

function App() {
  return (
    <div id="app">
      <Routes>
        <Route path="/" element={<Navi />}>
          <Route index element={<Homepage />}></Route>
          <Route path="/signup" element={<SignUp />}></Route>
          <Route path="/signin" element={<SignIn />}></Route>
        </Route>
        <Route path="*" element={<ErrorPage />}></Route>
      </Routes>
    </div>
  );
}

export default App;

3. 调整服务器端的HTML模板

服务器端需要完整的HTML结构,同时要引入客户端打包后的文件(浏览器无法直接解析未打包的index.js):

修改后的server.jsx:

import express from "express";
import ReactDOMServer from "react-dom/server";
import { StaticRouter } from "react-router-dom/server";
import App from "../src/App";
import path from "path";

const app = express();

// 静态资源托管,指向客户端打包后的目录
app.use(express.static(path.resolve(__dirname, "../build")));

app.get("*", (req, res) => {
  const appHtml = ReactDOMServer.renderToString(
    <StaticRouter location={req.url}>
      <App />
    </StaticRouter>
  );

  const fullHtml = `
    <!DOCTYPE html>
    <html>
      <head>
        <title>Meetings App</title>
        <link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css">
        <link rel="stylesheet" href="/index.css">
      </head>
      <body>
        <div id="root">${appHtml}</div>
        <script src="/static/js/main.[hash].js"></script>
      </body>
    </html>
  `;

  res.send(fullHtml);
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
  console.log(`server running on port ${PORT}`);
});

4. 客户端打包

确保先打包客户端代码:

npm run build

然后启动服务器:

npm run start:server

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:24:53