当前位置: 首页 > 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.gsyq.cn/news/1598336.html

相关文章:

  • Xilinx FIFO Generator AXI Stream模式实战:从配置到仿真验证
  • 2026最新整理 适合学生使用的高评价英语听力平台推荐清单
  • 终极Windows 11精简指南:使用tiny11builder快速创建纯净系统镜像
  • 利用Docker Compose一键部署DzzOffice与OnlyOffice私有云办公平台
  • MPLS LDP协议深度解析:从消息交互到会话状态机的实战指南
  • PostgreSQL数据文件损坏:从“read only 0 of 8192 bytes”错误到精准修复
  • Fast DDS之Domain隔离与Participant通信机制
  • Ubuntu 20.04下Gazebo仿真环境搭建与SLAM建图导航实战
  • 售前方案能不能用Codex和Claude半自动生成?客户需求到报价说明实战
  • 数据分析转大模型:真实项目中的关键步骤
  • 英飞凌AURIX平台嵌入式开发实战:从资源获取到多环境移植
  • 如何在Windows系统获得Apple触控板完美体验:mac-precision-touchpad驱动终极指南
  • 【Unity】官方API加持:SplashScreen.Stop()全平台跳过启动Logo实战解析
  • 【C 语言】文件操作 ( fread 函数进阶:缓冲区策略与错误处理 )
  • YimMenu完整指南:3步安装免费GTA5辅助工具并安全使用
  • 从零搭建汇编开发环境:DOSBox配置与核心调试实战
  • 渗透测试全流程实战:从信息收集到报告撰写的完整作战地图
  • 3个步骤让Windows原生运行安卓应用:APK安装器深度体验指南
  • 终极B站体验:PiliPlus跨平台第三方客户端的5大核心优势
  • Rimworld Mod开发指南:About文件——从零到一的Mod身份与兼容性设计
  • 终极免费抖音批量下载指南:如何快速保存无水印高清视频
  • Web安全测试实战指南:从SQL注入到XSS的手动漏洞挖掘与验证
  • 高级 RAG 范式:Self-RAG、CRAG、GraphRAG、Agentic RAG 到底解决什么问题?
  • FileBrowser批量下载功能:告别文件管理中的“逐个下载“噩梦
  • 从QStyle到自定义Style:Qt界面定制核心虚函数实战解析与流程图解
  • 30N03-ASEMI中低压大功率通用王者30N03
  • 宜春黄金白银回收铂金旧金回收无套路门店 TOP 榜单 实地测评资料整理
  • 大学物理的规范性作答:从符号表达到数值计算的标准化实践
  • RA8T2外部总线接口配置详解:从时序计算到实战避坑指南
  • 大模型MoE架构原理与实战:专家路由如何实现万亿参数高效推理