vue3
1. 项目结构
src/ ├── entry-server.js # 服务端入口 ├── entry-client.js # 客户端入口(水合) ├── App.vue server.js # Node服务 index.html # HTML模板2. 关键代码
server.js(Node 服务)
import express from 'express' import { createSSRApp } from 'vue' import { renderToString } from '@vue/server-renderer' import App from './App.vue' const app = express() app.get('*', async (req, res) => { // 1. 创建Vue实例(服务端) const vueApp = createSSRApp(App) // 2. 渲染组件 → HTML字符串 const html = await renderToString(vueApp) // 3. 返回完整页面,注入渲染出来的html res.send(` <!DOCTYPE html> <html> <body> <!-- 服务端渲染生成的HTML --> <div id="app">${html}</div> <!-- 客户端代码,用来水合激活交互 --> <script src="/client.js"></script> </body> </html> `) }) app.listen(3000, () => console.log('ssr server 3000'))entry-client.js(客户端水合)
import { createSSRApp } from 'vue' import App from './App.vue' const app = createSSRApp(App) // hydrate:水合,把服务端静态HTML绑定事件,变成可交互页面 app.mount('#app', true)App.vue
<template> <div> <h1>Vue SSR页面</h1> <p>{{ msg }}</p> <button @click="clickHandle">点我</button> </div> </template> <script setup> const msg = '服务端渲染内容' const clickHandle = () => alert('按钮交互') </script>3. SSR 核心流程
- 用户请求页面 → Node 服务启动 Vue 应用
renderToString在服务端执行组件,生成静态 HTML 字符串- HTML 直接返回浏览器,首屏立刻看到内容(利于 SEO、首屏加载)
- 浏览器加载客户端 js,执行
hydrate水合 - 水合:对比已有 DOM,绑定事件,页面拥有交互能力
4. 两个核心概念区分
- SSR(服务端渲染):
renderToString,输出 HTML 字符串,无交互 - 水合 (hydration):客户端复用已有 DOM,附加事件,避免重新创建 DOM
5. 极简面试一句话总结
SSR 在 Node 服务端运行 Vue 组件,通过renderToString将组件编译为 HTML 返回浏览器,实现快速首屏展示;客户端执行水合,为静态 DOM 绑定交互事件。
补充:和 SPA 区别
SPA:浏览器下载 js 后才渲染页面,空白屏; SSR:浏览器直接收到完整 HTML,首屏有内容,SEO 友好。
react
项目最简结构
server.js # Node服务 src/ ├── App.jsx # 组件 ├── entry-server.js # 服务端入口 └── entry-client.js # 客户端水合入口 index.html # HTML模板1. App.jsx
export default function App() { return ( <div> <h1>React SSR Demo</h1> <p>服务端渲染出来的文字</p> <button onClick={() => alert('水合成功,可以点击!')}>点击测试</button> </div> ) }2. entry-server.js(服务端渲染代码)
import { renderToString } from 'react-dom/server' import App from './App' export function render() { // 将组件渲染成HTML字符串 return renderToString(<App />) }3. entry-client.js(客户端水合)
import { hydrateRoot } from 'react-dom/client' import App from './App' // 复用服务端输出的DOM,绑定事件(水合) hydrateRoot(document.getElementById('root'), <App />)4. server.js(核心 Node 服务)
import express from 'express' import { render } from './src/entry-server.js' const app = express() // 托管打包后的客户端js静态资源 app.use(express.static('./dist/client')) app.get('*', async (req, res) => { // 1. 服务端渲染组件,得到html字符串 const appHtml = render() // 2. 返回完整HTML,注入渲染内容 + 引入客户端水合js const html = ` <!DOCTYPE html> <html> <body> <div id="root">${appHtml}</div> <!-- 客户端打包产物,用来水合 --> <script src="/entry-client.js"></script> </body> </html> ` res.send(html) }) app.listen(3000, () => console.log('React SSR 运行在3000'))完整执行流程
- 浏览器访问页面 → Node 服务调用
renderToString(<App/>),生成静态 HTML - 返回 HTML 给浏览器,页面直接展示内容(此时按钮不能点击)
- 浏览器加载
entry-client.js - 执行
hydrateRoot,对比现有 DOM,绑定事件,页面拥有交互
React SSR 和 Vue3 SSR 关键相同点
- 都需要两份入口文件:服务端渲染入口、客户端水合入口
- 服务端:只输出 HTML 字符串,不挂载 DOM
- 客户端:执行水合 hydrate,复用已有 DOM,添加交互
面试高频区分
✅ Vue3:renderToString+createSSRApp().mount('#app', true)✅ React18:renderToString+hydrateRoot