纯前端html页面无法直接做ssr,因为ssr需在node.js中执行js渲染逻辑生成html字符串,而静态index.html仅作模板;浏览器端html无js执行环境、无数据注入与路由能力,服务端需可import的组件模块及render函数。

纯前端 HTML 页面无法直接做 SSR——SSR 的核心是「在 Node.js 环境中执行 JavaScript 渲染逻辑,生成完整 HTML 字符串并返回给浏览器」,所以你手头那个静态 index.html 文件本身不参与 SSR 过程,它只是最终输出结果的模板或占位目标。
为什么不能直接用原生 HTML 文件做 SSR
SSR 不是“把 HTML 文件发过去”,而是“运行 JS 代码,动态拼出 HTML”。浏览器里打开的 index.html 没有 JS 执行环境,也没有数据注入、路由匹配、组件生命周期这些能力。服务端需要的是能被 require() 或 import 的模块(比如 React 组件、Vue SFC、或一个导出 render() 函数的文件)。
常见错误现象:
- 把
fs.readFileSync('./index.html')当成 SSR——这只是静态文件读取,没渲染、没数据、没 hydration 能力 - 在 Express 中
res.send(htmlString)前没调用任何渲染函数——等于裸发 HTML,和 SSR 无关 - 用
document.getElementById在服务端运行——Node.js 没有document,会直接报ReferenceError: document is not defined
React + Express 最简 SSR 实现要点
关键不是“怎么写 HTML”,而是“怎么让 React 组件在服务端吐出字符串”。你需要:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 一个可服务端导入的根组件(比如
App.jsx),不含浏览器专属 API(如window、localStorage) -
react-dom/server提供的renderToString()或renderToPipeableStream() - Express 路由中组装上下文:把初始数据、URL、请求头等传进组件(常通过
context或 props) - 把渲染结果塞进 HTML 模板的
<div id="root"></div>里,并内联window.__INITIAL_STATE__供客户端水合(hydration)用
示例片段(非完整代码,仅示意流程):
const { renderToString } = require('react-dom/server');
const App = require('./src/App').default;
app.get('*', (req, res) => {
const html = renderToString(<app url="{req.url}"></app>);
const initialState = { url: req.url };
res.send(`
<div id="root">${html}</div>
<script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}</script><script src="/client.js">
`);
});
</script>
Vue 3 的 createSSRApp 和常见陷阱
Vue 3.2+ 内置 SSR 支持,但必须用 createSSRApp 替代 createApp,否则组件里的 onMounted、useRouter 等钩子会在服务端执行报错。
-
createSSRApp会自动跳过浏览器专属生命周期(如onMounted),但不会帮你屏蔽document调用——你仍需手动守卫 - 异步数据获取必须放在
setup()或asyncData()(Nuxt 风格)中,不能依赖onBeforeMount,因为服务端不触发它 - 服务端渲染的组件不能使用
ref()直接操作 DOM,也不能依赖$refs——它们在服务端为undefined - 路径别名(如
@/components)在 Node.js 中默认不解析,需用esbuild或vite-node预构建,或改用相对路径
SSR 输出的 HTML 必须满足 hydration 可行性
客户端 JS 加载后调用 hydrateRoot(React)或 app.mount()(Vue),会对比服务端 HTML 和客户端虚拟 DOM。只要有一处不一致(比如服务端渲染了 <div>Hello</div>,客户端却因未判断环境渲染成 <span>Hello</span>),就会触发警告甚至丢弃整个 DOM 树重绘。
- 确保服务端和客户端使用同一份组件代码、同一份数据源(避免服务端用 mock,客户端才拉 API)
- 所有条件渲染逻辑(
v-if/{condition && <x></x>})必须在两端得到相同判定结果;时间、随机数、Math.random()、new Date()都是危险源 - 样式注入顺序要一致:CSS-in-JS 库(如 Emotion、Styled Components)需启用 SSR 支持,否则服务端无 class,客户端才加,hydration 失败
最常被忽略的一点:开发时热更新(HMR)通常只作用于客户端 bundle,服务端代码修改后必须手动重启 Node 进程,否则看到的永远是旧 HTML —— 这不是 bug,是机制使然。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










