进阶篇一 Nuxt4 SSR 原理:服务端渲染到底做了什么
文章目录
- 一、什么是 SSR
- 二、Nuxt SSR 流程
- 三、服务端渲染过程
- 1. 路由匹配
- 2. 执行 asyncData
- 3. 渲染组件
- 4. 生成完整页面
- 四、Hydration 是什么
- Hydration 过程:
- Hydration 不匹配错误
- 五、数据传递机制
- 六、只在客户端执行
- 七、只在服务端执行
- 八、服务端上下文
- 九、SSR 的代价
- 十、调试 SSR
- 总结
面试经常被问:“说说 SSR 的原理”。很多人只能答出"在服务端渲染页面",但具体怎么渲染、数据怎么传递、hydration 是什么……一问三不知。今天我们来彻底搞懂 SSR。
一、什么是 SSR
SSR(Server-Side Rendering),服务端渲染。简单说:页面在服务器生成 HTML,浏览器直接显示。
对比 CSR(Client-Side Rendering):
CSR 流程: 1. 浏览器请求页面 2. 服务器返回空 HTML + JS 3. 浏览器下载执行 JS 4. JS 请求数据 5. JS 渲染页面 SSR 流程: 1. 浏览器请求页面 2. 服务器执行 JS 获取数据 3. 服务器渲染 HTML 4. 返回完整 HTML 5. 浏览器直接显示SSR 的好处:
- 首屏快:不需要等 JS 执行
- SEO 友好:爬虫能直接看到内容
- 社交分享:能正确抓取 meta 信息
二、Nuxt SSR 流程
Nuxt 的 SSR 流程更复杂一些:
┌─────────────┐ │ Browser │ └──────┬──────┘ │ 1. 请求页面 ▼ ┌─────────────┐ │ Nuxt Server │ ──> 2. 匹配路由 │ (Nitro) │ ──> 3. 执行中间件 └──────┬──────┘ ──> 4. 执行 asyncData │ 5. 渲染 Vue 组件 │ 6. 返回 HTML ▼ ┌─────────────┐ │ Browser │ ──> 7. 显示 HTML └──────┬──────┘ ──> 8. 加载 JS │ 9. Hydration ▼ ┌─────────────┐ │ Interactive │ ──> 10. 页面可交互 └─────────────┘三、服务端渲染过程
当用户访问/article/123时:
1. 路由匹配
Nuxt 根据请求路径匹配对应的页面组件:
// 请求 /article/123// 匹配 pages/article/[id].vue2. 执行 asyncData
在服务端执行数据获取:
<script setup lang="ts"> const route = useRoute() const { data: article } = await useFetch(`/api/articles/${route.params.id}`) </script>useFetch在服务端会真实发起请求,获取数据。
3. 渲染组件
Vue 将组件渲染成 HTML 字符串:
<article><h1>文章标题</h1><p>文章内容...</p></article>4. 生成完整页面
Nuxt 将组件 HTML 和数据打包成完整页面:
<!DOCTYPEhtml><html><head><title>文章详情</title><!-- 样式、脚本 --></head><body><divid="__nuxt"><article><h1>文章标题</h1><p>文章内容...</p></article></div><!-- 关键:数据注入 --><script>window.__NUXT__={data:{article:{id:123,title:'文章标题',...}}}</script></body></html>四、Hydration 是什么
Hydration(水合)是 SSR 的关键步骤:
服务端返回的是:静态 HTML(没有事件绑定) Hydration 做的是:给静态 HTML "注水",让它变成可交互的 Vue 应用Hydration 过程:
- 浏览器加载 Vue 运行时
- Vue 根据
window.__NUXT__恢复状态 - Vue 将事件绑定到已有 DOM 上
- 页面变得可交互
<template> <!-- 服务端渲染时:生成静态 HTML --> <button @click="count++">{{ count }}</button> <!-- Hydration 后:点击事件生效 --> </template>Hydration 不匹配错误
如果服务端渲染的 HTML 和客户端预期的不一样,会报错:
[Vue warn]: Hydration node mismatch常见原因:
<script setup lang="ts"> // ❌ 服务端和客户端时间不同 const time = new Date().toLocaleString() // ❌ 服务端没有 localStorage const theme = localStorage.getItem('theme') // ❌ 随机值每次不同 const random = Math.random() </script>解决方法:
<script setup lang="ts"> // ✅ 用 onMounted 处理客户端特有逻辑 const time = ref('') onMounted(() => { time.value = new Date().toLocaleString() }) // ✅ 判断环境 const theme = ref('light') if (import.meta.client) { theme.value = localStorage.getItem('theme') || 'light' } </script>五、数据传递机制
服务端获取的数据如何传给客户端?
Nuxt 使用window.__NUXT__对象:
<!-- 服务端注入的数据 --><script>window.__NUXT__={serverRendered:true,data:{// useFetch 获取的数据'/api/articles/123':{id:123,title:'...'}},state:{// useState 的状态user:{id:1,name:'...'}}}</script>客户端初始化时直接使用这些数据,不再重复请求。
六、只在客户端执行
有些代码不需要在服务端执行:
<script setup lang="ts"> // 方式一:onMounted onMounted(() => { console.log('只在客户端执行') }) // 方式二:判断环境 if (import.meta.client) { console.log('只在客户端执行') } // 方式三:ClientOnly 组件(模板中) </script> <template> <ClientOnly> <div>这部分不会在服务端渲染</div> </ClientOnly> </template>七、只在服务端执行
有些代码只需要在服务端执行:
<script setup lang="ts"> if (import.meta.server) { // 访问数据库、读取文件系统等 const event = useRequestEvent() const headers = getHeaders(event) console.log('请求头:', headers) } </script>八、服务端上下文
在服务端可以访问请求信息:
<script setup lang="ts"> if (import.meta.server) { const event = useRequestEvent() // 请求 URL const url = getRequestURL(event) // 请求头 const headers = getHeaders(event) // Cookie const cookies = parseCookies(event) // 客户端 IP const ip = getRequestIP(event) // 请求方法 const method = getMethod(event) } </script>九、SSR 的代价
SSR 不是银弹,有代价:
| 优点 | 缺点 |
|---|---|
| 首屏快 | 服务器压力大 |
| SEO 友好 | 开发复杂度高 |
| 社交分享 | 需要处理 Hydration |
| 不能用浏览器特有的 API |
选择建议:
- 内容网站、博客、电商 → SSR
- 后台管理、工具应用 → CSR
- 两者结合 → 部分页面 SSR
十、调试 SSR
查看服务端渲染结果:
// 在页面中console.log('服务端渲染:',import.meta.server)console.log('客户端渲染:',import.meta.client)在终端看到的就是服务端输出,在浏览器控制台看到的是客户端输出。
总结
SSR 核心概念:
| 概念 | 说明 |
|---|---|
| 服务端渲染 | 在服务器生成 HTML |
| Hydration | 给静态 HTML 绑定事件 |
__NUXT__ | 数据传递载体 |
import.meta.server | 判断服务端环境 |
import.meta.client | 判断客户端环境 |
理解 SSR 原理,才能写出正确的代码,避免 Hydration 错误。下一篇聊聊渲染模式选择。
相关文章
入门篇三:Nuxt4组件自动导入:写代码少敲一半字
入门篇二:Nuxt 4路由自动生成:告别手动配置路由的日子
延伸阅读
nuxt4完整系列,持续更新中。。,欢迎来逛逛
内容有帮助?点赞、收藏、关注三连!评论区等你 💪
