vue3
1. 项目结构
javascript
src/
├── entry-server.js # 服务端入口
├── entry-client.js # 客户端入口(水合)
├── App.vue
server.js # Node服务
index.html # HTML模板
2. 关键代码
server.js(Node 服务)
javascript
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(客户端水合)
javascript
import { createSSRApp } from 'vue'
import App from './App.vue'
const app = createSSRApp(App)
// hydrate:水合,把服务端静态HTML绑定事件,变成可交互页面
app.mount('#app', true)
App.vue
javascript
<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
项目最简结构
javascript
server.js # Node服务
src/
├── App.jsx # 组件
├── entry-server.js # 服务端入口
└── entry-client.js # 客户端水合入口
index.html # HTML模板
1. App.jsx
javascript
export default function App() {
return (
<div>
<h1>React SSR Demo</h1>
<p>服务端渲染出来的文字</p>
<button onClick={() => alert('水合成功,可以点击!')}>点击测试</button>
</div>
)
}
2. entry-server.js(服务端渲染代码)
javascript
import { renderToString } from 'react-dom/server'
import App from './App'
export function render() {
// 将组件渲染成HTML字符串
return renderToString(<App />)
}
3. entry-client.js(客户端水合)
javascript
import { hydrateRoot } from 'react-dom/client'
import App from './App'
// 复用服务端输出的DOM,绑定事件(水合)
hydrateRoot(document.getElementById('root'), <App />)
4. server.js(核心 Node 服务)
javascript
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