当前位置: 首页 > news >正文

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-rendererrenderToNodeStream+ 组件缓存:

// 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 和浏览器。

http://www.jsqmd.com/news/1086385/

相关文章:

  • ESP32 SSD1306 OLED显示驱动深度解析:5大实战优化策略与高级应用指南
  • ComfyUI-MimicMotionWrapper终极指南:3步实现专业级AI动作迁移
  • 迁移学习成败的关键:数据集类别设计的底层逻辑
  • 沃罗诺伊图(Voronoi):从自然到算法的艺术【实践篇】
  • 每日热门skill:给AI装上一部电话!PollyReach让OpenClaw Agent打通物理世界「最后一公里」
  • 终极Windows 11精简指南:使用tiny11builder快速创建纯净系统镜像
  • Xilinx FIFO Generator AXI Stream模式实战:从配置到仿真验证
  • 利用Docker Compose一键部署DzzOffice与OnlyOffice私有云办公平台
  • 2026最新整理 适合学生使用的高评价英语听力平台推荐清单
  • MPLS LDP协议深度解析:从消息交互到会话状态机的实战指南
  • 论文写作工具推荐:4款主流AI工具横评,总有一款适合你
  • RC/RL并联电路:从阻抗计算到参数反演的实用指南
  • 【PDF工具篇】Windows平台PDF笔记神器Drawboard PDF旧版获取与部署指南
  • 072、Pandas 数据清洗:缺失值处理、类型转换、字符串操作、apply 家族
  • 从“边界”视角重识C++ set的lower_bound与upper_bound
  • OMPL中BIT*算法核心流程与关键模块解析
  • Steam游戏自动破解器:终极指南与完整解决方案
  • JSON转Excel实际应用场景案例
  • HIS医院信息系统:微服务架构实践与医疗数字化转型方案
  • ENVI实战:为无地理参考的栅格影像精准注入空间坐标
  • PostgreSQL数据文件损坏:从“read only 0 of 8192 bytes”错误到精准修复
  • Fast DDS之Domain隔离与Participant通信机制
  • LSI MegaRAID实战:从零配置硬RAID到系统挂载
  • 国内各大招聘平台分类汇总|HR选型全指南,附低成本直聘渠道推荐
  • 550+免费RPG Maker插件库:从新手到专家的完整游戏开发解决方案
  • 终极WPF界面开发解决方案:HandyControls控件库完整实战指南
  • 明日方舟自动化终极指南:3分钟掌握Arknights-Mower智能基建管理
  • 微信好友检测终极指南:3分钟发现谁已悄悄删除你
  • 售前方案能不能用Codex和Claude半自动生成?客户需求到报价说明实战
  • AI私域电商品牌实测排行:2026年七大维度对比与场景适配