React SSR场景下Node.js无需转译使用ES6模块的方法
如何在无需运行时转译的情况下实现React SSR(使用ES6模块)
核心思路是利用Node.js原生对ES模块的支持,配合TypeScript/Flow在构建阶段处理类型标注和JSX转换(这不属于运行时转译),让最终产出的代码能直接被Node.js执行。
步骤1:开启Node.js的ES模块支持
Node.js从v14.13.0开始原生支持ES模块,只需在项目根目录的package.json中添加一行配置:
{ "type": "module" }
之后所有.js文件都会被Node.js当作ES模块处理,直接识别import/export语法,完全不需要babel-node这类运行时转译工具。
步骤2:用TypeScript/Flow处理类型与JSX(构建时完成)
你的需求是仅去除类型标注,保留ES模块语法,所以需要通过TS/Flow的构建工具把带类型的JSX/TSX代码转换成纯ES模块的JavaScript(同时把JSX转成Node.js能识别的React.createElement调用)。
如果用TypeScript:
- 配置
tsconfig.json关键项:
{ "compilerOptions": { "module": "ESNext", // 输出ES模块格式 "target": "ES2020", // 匹配Node.js支持的ES版本(可根据你使用的Node版本调整) "jsx": "react", // 把JSX转成React.createElement调用 "outDir": "./dist", // 编译后的代码输出目录 "strict": true, // 可选,开启严格类型检查 "moduleResolution": "node" // 按照Node.js的模块解析规则处理路径 }, "include": ["src/**/*"] }
- 运行编译命令:
tsc
编译完成后,src目录下的组件会被转换成dist目录里的纯ES模块JS文件,已经去掉了类型标注,JSX也转成了可直接执行的代码。
如果用Flow:
- 安装必要工具:
npm install --save-dev flow-bin flow-remove-types @babel/core @babel/plugin-transform-react-jsx
- 创建
.babelrc配置JSX转换:
{ "plugins": ["@babel/plugin-transform-react-jsx"] }
- 添加编译脚本到
package.json:
{ "scripts": { "build": "flow remove-types src/ --out-dir dist/ && babel dist/ --out-dir dist/" } }
这个脚本会先去掉Flow类型标注,再把JSX转成React.createElement调用,最终输出到dist目录的是纯ES模块代码。
步骤3:编写服务端SSR代码(纯ES模块)
服务端代码本身也用ES模块编写,直接导入编译后的React组件进行渲染:
import express from 'express'; import React from 'react'; import { renderToString } from 'react-dom/server'; // 导入编译后的ES模块组件(注意要加完整的.js扩展名) import App from './dist/App.js'; const app = express(); // 静态文件托管(用于客户端hydration的脚本) app.use(express.static('client/dist')); app.get('/', (req, res) => { // 服务端渲染React组件为HTML字符串 const appHtml = renderToString(<App />); // 返回完整的HTML页面,包含SSR内容和客户端激活脚本 res.send(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>React SSR Demo</title> </head> <body> <div id="root">${appHtml}</div> <!-- 客户端脚本也要是ES模块格式 --> <script type="module" src="/App.js"></script> </body> </html> `); }); app.listen(3000, () => { console.log('Server is running at http://localhost:3000'); });
步骤4:处理客户端hydration代码
客户端代码同样需要编译成ES模块格式(和服务端用相同的TS/Flow配置),用于把SSR输出的HTML激活成可交互的React应用:
// client/src/App.jsx import React from 'react'; import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; const root = document.getElementById('root'); hydrateRoot(root, <App />);
编译后放到client/dist目录,通过服务端的静态文件托管访问即可。
关键注意事项
- 文件扩展名:在ES模块中,导入文件必须加上完整的扩展名(比如
.js),Node.js不会自动补全。TS/Flow的编译工具会帮你处理这一点,但手动编写的导入要留意。 - Node.js版本:确保使用v14.13.0或更高版本的Node.js,才能稳定支持ES模块特性。
- CommonJS依赖兼容:大部分npm包目前还是CommonJS格式,Node.js的ES模块系统可以直接import这些包(比如
import express from 'express'),无需额外处理。
这样整个流程下来,服务端运行的是已经构建好的纯ES模块代码,没有任何运行时转译,完全符合你的需求。
内容的提问来源于stack exchange,提问作者garyx
相关产品推荐
相关产品推荐

