[Lv3] Nuxt 3 性能优化:Bundle Size、SSR 速度与图片优化
Nuxt 3 性能优化全攻略:从 Bundle Size 瘦身、SSR 速度优化到图片加载策略,打造极致性能体验。
1. 面试回答主轴
- Bundle Size 优化:分析 (
nuxi analyze)、拆分 (SplitChunks)、Tree Shaking、延迟加载 (Lazy Loading)。 - SSR 速度优化 (TTFB):Redis 缓存、Nitro Cache、减少阻塞式 API 调用、Streaming SSR。
- 图片优化:
@nuxt/image、WebP 格式、CDN、Lazy Loading。 - 大量数据优化:虚拟滚动 (Virtual Scrolling)、无限滚动 (Infinite Scroll)、分页 (Pagination)。
2. 如何减少 Nuxt 3 的 Bundle Size?
2.1 诊断工具
首先,必须知道瓶颈在哪里。使用 nuxi analyze 来可视化 Bundle 结构。
npx nuxi analyze
这会产生一个报告,显示哪些套件占用了最大空间。
2.2 优化策略
1. Code Splitting (代码拆分)
Nuxt 3 默认已经基于路由 (Route-based) 做 Code Splitting。但对于大型套件(如 ECharts, Lodash),我们需要手动优化。
Nuxt Config 配置 (Vite/Webpack):
// nuxt.config.ts
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks(id) {
// 将 node_modules 中的大型套件拆分出来
if (id.includes('node_modules')) {
if (id.includes('lodash')) return 'lodash';
if (id.includes('echarts')) return 'echarts';
}
},
},
},
},
},
});
2. Tree Shaking & 按需引入
确保只引入需要的模块,而不是整个套件。
// ❌ 错误:引入整个 lodash
import _ from 'lodash';
_.debounce(() => {}, 100);
// ✅ 正确:只引入 debounce
import debounce from 'lodash/debounce';
debounce(() => {}, 100);
// ✅ 推荐:使用 vueuse (Vue 专用且 Tree-shakable)
import { useDebounceFn } from '@vueuse/core';
3. 组件 Lazy Loading
对于非首屏需要的组件,使用 Lazy 前缀进行动态导入。
<template>
<div>
<!-- 只有当 show 为 true 时才会加载该组件代码 -->
<LazyHeavyComponent v-if="show" />
</div>
</template>
4. 移除不必要的 Server-side 套件
确保只在 Server 端使用的套件(如数据库驱动、fs 操作)不会被打包到 Client 端。Nuxt 3 会自动处理 .server.ts 结尾的文件,或使用 server/ 目录。
3. 如何优化 SSR 速度 (TTFB)?
3.1 为什么 TTFB 会过长?
TTFB (Time To First Byte) 是 SSR 性 能的关键指标。过长的原因通常是:
- API 响应慢:Server 需要等待后端 API 返回数据才能渲染 HTML。
- 串行请求:多个 API 请求依序执行,而非并行。
- 繁重的计算:Server 端执行了过多 CPU 密集型任务。
3.2 优化方案
1. Server-Side Caching (Nitro Cache)
使用 Nitro 的缓存功能,将 API 响应或渲染结果缓存起来。
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// 首页缓存 1 小时 (SWR: Stale-While-Revalidate)
'/': { swr: 3600 },
// 产品页缓存 10 分钟
'/products/**': { swr: 600 },
// API 缓存
'/api/**': { cache: { maxAge: 60 } },
},
});
2. 并行请求 (Parallel Fetching)
使用 Promise.all 并行发送多个请求,而不是 await 一个接一个。
// ❌ 慢:串行执行 (总时间 = A + B)
const { data: user } = await useFetch('/api/user');
const { data: posts } = await useFetch('/api/posts');
// ✅ 快:并行执行 (总时间 = Max(A, B))
const [{ data: user }, { data: posts }] = await Promise.all([
useFetch('/api/user'),
useFetch('/api/posts'),
]);
3. 延迟非关键数据 (Lazy Fetching)
首屏不需要的数据,可以在 Client 端再加载 (lazy: true),避免阻塞 SSR。
// 评论数据不需要 SEO,可以在 Client 端加载
const { data: comments } = await useFetch('/api/comments', {
lazy: true,
server: false, // 甚至完全不在 Server 端执行
});
4. Streaming SSR (实验性)
Nuxt 3 支持 HTML Streaming,可以边渲染边返回,让用户更快看到内容。
4. Nuxt 3 图片优化
4.1 使用 @nuxt/image
官方模块 @nuxt/image 是最佳解,提供:
- 自动格式转换:自动转为 WebP/AVIF。
- 自动缩放:根据屏幕大小产生对应尺寸图片。
- Lazy Loading:内建懒加载。
- CDN 集成:支持 Cloudinary, Imgix 等多种 Provider。
4.2 实现示例
npm install @nuxt/image
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
// 默认选项
format: ['webp'],
},
});
<template>
<!-- 自动转换为 webp,宽度 300px,并启用 lazy load -->
<NuxtImg
src="/hero.jpg"
format="webp"
width="300"
loading="lazy"
placeholder
/>
</template>
5. 大量数据的分页与滚动
5.1 方案选择
针对大量数据(如 10,000 条商品),主要有三种策略,需考量 SEO:
| 策略 | 适合场景 | SEO 友好度 |
|---|---|---|
| 传统分页 (Pagination) | 电商列表、文章列表 | 最佳 |
| 无限滚动 (Infinite Scroll) | 社交动态、图片墙 | 需特殊处理 |
| 虚拟滚动 (Virtual Scroll) | 复杂报表、超长列表 | 内容不在 DOM 中 |