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-memoVue 3.2缓存 DOMtemplate !-- v-memo 会缓存这个 DOM 片段依赖的值不变就不重新渲染 -- div v-memo[product.id, product.price] h3{{ product.name }}/h3 p价格{{ product.price }}/p p库存{{ product.stock }}/p /div /template3. 构建层面的优化优化一代码分割按需加载// 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.jsVite 项目 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-age60; }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 relpreload href/assets/app.js asscript link relpreload href/assets/app.css asstyle !-- DNS 预解析 -- link reldns-prefetch href//api.example.com !-- 预连接 -- link relpreconnect hrefhttps://api.example.com /head6. 数据层面的优化优化一减少数据请求// ❌ 错误每个组件都单独请求 // 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 和浏览器。