如何通过Express路由渲染React组件实现SSR并解决JSX报错
问题根因
报错本质是JSX未被正确转译,来自两个配置错误:
- 原dev命令参数顺序错误,nodemon未正确将执行权交给babel-node,JSX代码直接被Node.js原生解析,Node不识别
<开头的JSX标签直接抛出语法错误。 - 开启
"type": "module"ES模块模式后,命令行直接传--presets env,react的简写写法,和当前babel版本、ESM模式不兼容,转译预设根本没加载。
另外直接返回renderToString的输出也不规范,这只是组件的HTML片段,没有完整文档结构,浏览器拿到后会触发怪异模式渲染。
最简实现方案
全程不需要引入额外路由框架,所有路由完全由Express管控,页面路由、API路由可自由定义,不会出现路由逻辑碎片化问题。
1. 修正Babel配置
在项目根目录新建babel.config.json,替代命令行传presets的不稳定写法:
{ "presets": [ "@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }] ] }
配置runtime: "automatic"后,组件文件不需要手动写import React from 'react',可以简化代码。
2. 修正package.json启动脚本
把dev命令改成正确的参数格式,--是nodemon传递后续执行参数的分隔符:
{ "name": "project", "version": "1.0.0", "main": "index.js", "type": "module", "scripts": { "dev": "nodemon --exec babel-node index.js" }, "license": "MIT", "dependencies": { "@babel/node": "^7.18.6", "@babel/preset-env": "^7.18.6", "@babel/preset-react": "^7.18.6", "express": "^4.18.1", "nodemon": "^2.0.19", "react": "^18.2.0", "react-dom": "^18.2.0" } }
3. 改造服务入口逻辑
修改index.js,把组件渲染结果包裹在完整HTML文档中返回,API路由和页面路由平级编写即可,完全由Express统一调度。注意ESM模式下导入本地文件必须写全后缀:
import express from "express"; import ReactDOMServer from "react-dom/server"; import Home from "./Home.js"; const app = express(); const port = 3000; // 页面路由 app.get("/", (req, res) => { const pageContent = ReactDOMServer.renderToString(<Home />); res.send(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>首页</title> </head> <body> <div id="root">${pageContent}</div> </body> </html> `); }); // 后续API路由直接按Express常规写法添加即可,和页面路由互不干扰 // 示例API app.get("/api/info", (req, res) => { res.json({ code: 0, data: "hello api" }); }); app.listen(port, () => { console.log(`服务运行在 http://localhost:${port}`); });
4. 组件文件无需额外修改
Home.js保留原有代码即可正常运行:
export default function Home() { return ( <div> <h1>home</h1> <p>hello world</p> </div> ); }
补充说明
- 该配置为开发环境最简实现,执行
npm run dev即可正常访问,JSX修改后nodemon会自动重启服务。 - 如果后续需要给页面加客户端交互(比如绑定事件、响应组件状态),再额外补充客户端hydrate逻辑即可,当前配置已经可以实现纯展示类页面的SSR渲染。
- 生产环境建议把服务端代码用babel提前编译成普通JS再启动,不要用babel-node跑生产服务,性能表现更好。
内容的提问来源于stack exchange,提问作者jaxramus
相关产品推荐
相关产品推荐

