vue3优化SSR在哪
·
SSR 的优化是一个系统性工程,涉及代码层面、构建层面、服务器层面、网络层面等多个维度。
1. SSR 本身的性能瓶颈在哪?
在讲优化之前,先搞清楚 SSR 为什么慢:
普通 CSR:
浏览器请求 → 返回空白 HTML → 下载 JS → 执行 → 渲染
↓
瓶颈在:网络带宽(JS 体积)
SSR:
浏览器请求 → 服务器运行 Vue → 生成 HTML → 返回
↓
瓶颈在:服务器 CPU(每次请求都要渲染)
核心问题:SSR 把"渲染"从客户端搬到了服务器,服务器的 CPU 成了新的瓶颈。
2. 代码层面的优化
优化一:组件级别的缓存(缓存不常变的内容)
<!-- ❌ 不缓存:每次请求都重新渲染 -->
<template>
<div>
<Header /> <!-- 每个用户看到的都一样 -->
<ProductList /> <!-- 变化频繁 -->
<Footer /> <!-- 每个用户看到的都一样 -->
</div>
</template>
<!-- ✅ 缓存静态部分 -->
<template>
<div>
<!-- 用 v-once 缓存静态内容 -->
<Header v-once />
<ProductList />
<Footer v-once />
</div>
</template>
更高级的做法:使用 @vue/server-renderer 的 renderToNodeStream + 组件缓存:
// server.js
import LRU from 'lru-cache'
import { createRenderer } from '@vue/server-renderer'
// 创建组件缓存(缓存 1000 个组件,有效期 1 分钟)
const cache = new LRU({
max: 1000,
maxAge: 1000 * 60
})
const renderer = createRenderer({
cache
})
// 在组件中启用缓存
// ProductList.vue
export default {
name: 'ProductList',
serverCacheKey: () => {
// 根据数据生成缓存 key
return `product-list-${this.category}-${this.page}`
}
}
优化二:避免在组件中写"重"逻辑
// ❌ 错误:在 setup 中做大量计算(服务端每次请求都执行)
const expensiveData = computed(() => {
return hugeArray
.filter(x => x > 0)
.map(x => x * 2)
.sort((a, b) => a - b)
// ... 大量计算
})
// ✅ 正确:把计算挪到客户端
const expensiveData = ref(null)
onMounted(() => {
// 只在浏览器执行
expensiveData.value = hugeArray
.filter(x => x > 0)
.map(x => x * 2)
.sort((a, b) => a - b)
})
优化三:使用 v-memo(Vue 3.2+)缓存 DOM
<template>
<!-- v-memo 会缓存这个 DOM 片段,依赖的值不变就不重新渲染 -->
<div v-memo="[product.id, product.price]">
<h3>{{ product.name }}</h3>
<p>价格:{{ product.price }}</p>
<p>库存:{{ product.stock }}</p>
</div>
</template>
3. 构建层面的优化
优化一:代码分割(按需加载)
// router/index.js
const routes = [
{
path: '/',
component: Home // 立即加载
},
{
path: '/product/:id',
// ✅ 懒加载:只有访问时才加载这个组件
component: () => import('@/views/Product.vue')
},
{
path: '/checkout',
component: () => import('@/views/Checkout.vue')
}
]
优化二:只打包必要的代码
// vite.config.js(Vite 项目)
export default {
build: {
rollupOptions: {
output: {
// 手动分割 chunk
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'], // 第三方库单独打包
ui: ['element-plus'] // UI 库单独打包
}
}
}
}
}
优化三:启用 gzip 压缩
// vite.config.js
import compression from 'vite-plugin-compression'
export default {
plugins: [
compression({
algorithm: 'gzip',
threshold: 10240 // 10KB 以上的文件才压缩
})
]
}
4. 服务器层面的优化
优化一:启用缓存(最重要的优化!)
// server.js
import express from 'express'
import LRU from 'lru-cache'
const app = express()
const cache = new LRU({
max: 100, // 最多缓存 100 个页面
maxAge: 1000 * 60 // 缓存 1 分钟
})
app.get('*', async (req, res) => {
// 1. 检查缓存
const cacheKey = req.url
const cached = cache.get(cacheKey)
if (cached) {
// ✅ 命中缓存:直接返回,不渲染
return res.send(cached)
}
// 2. 未命中:渲染并缓存
const html = await renderSSR(req)
cache.set(cacheKey, html)
res.send(html)
})
优化二:使用流式渲染(Streaming)
// ❌ 普通渲染:等全部渲染完才返回
const html = await renderToString(app)
res.send(html)
// ✅ 流式渲染:边渲染边发送(用户更快看到内容)
import { renderToNodeStream } from '@vue/server-renderer'
const stream = renderToNodeStream(app)
stream.pipe(res) // 浏览器会逐步渲染
优化三:集群模式(利用多核 CPU)
// server.js
import cluster from 'cluster'
import os from 'os'
if (cluster.isMaster) {
// 主进程:fork 多个工作进程
const numCPUs = os.cpus().length
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
} else {
// 工作进程:运行 SSR 服务器
const app = express()
app.get('*', async (req, res) => {
// SSR 逻辑...
})
app.listen(3000)
}
优化四:使用 CDN 缓存静态资源
# nginx.conf
location /assets/ {
# 静态资源设置长期缓存
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
# HTML 不缓存或短缓存
expires 1m;
add_header Cache-Control "public, max-age=60";
}
5. 网络层面的优化
优化一:HTTP/2 Server Push(主动推送)
// server.js
import spdy from 'spdy' // HTTP/2 服务器
const server = spdy.createServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert')
}, app)
server.on('stream', (stream) => {
// 主动推送关键 CSS/JS
stream.pushStream({ path: '/assets/app.css' }, (err, pushStream) => {
pushStream.respond({ 'content-type': 'text/css' })
pushStream.end(fs.readFileSync('dist/assets/app.css'))
})
})
优化二:资源预加载
<!-- 在 HTML 模板中添加预加载 -->
<head>
<!-- 预加载关键资源 -->
<link rel="preload" href="/assets/app.js" as="script">
<link rel="preload" href="/assets/app.css" as="style">
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">
<!-- 预连接 -->
<link rel="preconnect" href="https://api.example.com">
</head>
6. 数据层面的优化
优化一:减少数据请求
// ❌ 错误:每个组件都单独请求
// Header.vue
const user = await fetch('/api/user')
// ProductList.vue
const products = await fetch('/api/products')
// Footer.vue
const config = await fetch('/api/config')
// ✅ 正确:在服务端一次性请求所有数据
export async function getServerData() {
const [user, products, config] = await Promise.all([
fetch('/api/user'),
fetch('/api/products'),
fetch('/api/config')
])
return { user, products, config }
}
// 然后通过 Pinia 或 props 传递给所有组件
优化二:只传输必要的数据
// ❌ 错误:返回全部数据
// 包含 createdAt, updatedAt, 描述...
const product = await db.product.findByPk(id)
// ✅ 正确:只返回需要的字段
const product = await db.product.findByPk(id, {
attributes: ['id', 'name', 'price', 'image'] // 只选需要的
})
7. Nuxt 3 自带优化(开箱即用)
如果你用 Nuxt 3,很多优化已经帮你做了:
| 优化项 | Nuxt 3 是否自动处理 |
|---|---|
| 代码分割 | ✅ 自动 |
| 路由懒加载 | ✅ 自动 |
| 静态资源压缩 | ✅ 自动 |
| 组件缓存 | ⚠️ 需配置 serverCacheKey |
| 流式渲染 | ✅ 自动(renderToNodeStream) |
| 预加载 | ✅ 自动生成 preload 标签 |
| 数据去重 | ✅ useFetch 自动去重 |
// Nuxt 3 中开启更多优化
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
// 使用更快的渲染引擎
preset: 'node-server',
// 启用缓存
cache: {
swr: true,
maxAge: 60
}
},
// 开启实验性功能
experimental: {
payloadExtraction: true, // 提取 payload,减少重复渲染
renderJsonPayloads: true // 使用 JSON 格式传输数据
}
})
8. 优化效果对比
| 优化手段 | 预期提升 | 实施难度 |
|---|---|---|
| 页面缓存(LRU) | ⬆️ 响应时间减少 70-90% | ⭐ 简单 |
| 流式渲染 | ⬆️ 首屏时间减少 30-50% | ⭐⭐ 中等 |
| 组件缓存 | ⬆️ 渲染时间减少 40-60% | ⭐⭐⭐ 较难 |
| 代码分割 | ⬇️ 首屏 JS 体积减少 50% | ⭐ 简单 |
| Gzip 压缩 | ⬇️ 传输体积减少 70% | ⭐ 简单 |
| 集群模式 | ⬆️ 吞吐量提升 200-400% | ⭐⭐ 中等 |
| CDN 缓存 | ⬇️ 服务器压力减少 80% | ⭐⭐ 中等 |
9. 实战优化清单
✅ 代码层面
□ 组件使用 v-once 缓存静态内容
□ 重计算放在 onMounted 中
□ 使用 v-memo 缓存 DOM
□ 路由组件懒加载
✅ 构建层面
□ 启用 gzip/brotli 压缩
□ 分割第三方库(vendor chunk)
□ 图片/字体等资源优化
✅ 服务器层面
□ 页面缓存(LRU)
□ 流式渲染
□ 启用 Node.js cluster
□ 使用 PM2 进程管理
✅ 网络层面
□ CDN 加速
□ 资源预加载(preload)
□ HTTP/2 或 HTTP/3
✅ 数据层面
□ 合并数据请求
□ 只传输必要字段
□ API 响应缓存
10. 一句话总结
SSR 优化的核心是"减少服务端渲染负担":能缓存的就缓存,能懒加载的就懒加载,能预加载的就预加载,把压力从服务器转移到 CDN 和浏览器。
更多推荐

所有评论(0)