Nuxt4 性能调优实战 Checklist

性能优化不是一次性工作,而是持续的过程。本章提供一份可执行的 Checklist——从构建产物分析到 Core Web Vitals 达标验证,每一项都给出具体操作步骤、配置代码和预期效果。按优先级排序:先做投入小收益大的(图片、字体、缓存),再做需要架构调整的(渲染策略、代码拆分)。

1. 构建产物分析

1.1 使用 nuxi analyze

npx nuxi analyze

运行后自动打开浏览器,展示 bundle 的 treemap 可视化。重点关注:

  • 超过 50KB 的 chunk:是否可以拆分或懒加载
  • 重复依赖:同一个库是否被打包了多次
  • 未使用的代码:某些库只用了一个函数,却打包了整个库

1.2 查看 SSR payload 大小

// 临时添加到 app.vue,查看 payload 大小
onMounted(() => {
  const payload = document.querySelector('#__NUXT_DATA__')
  if (payload) {
    console.log(`Payload size: ${(payload.textContent?.length ?? 0 / 1024).toFixed(1)} KB`)
  }
})

目标:payload 控制在 50KB 以下。超过 100KB 需要用 transformpick 裁剪。

1.3 Lighthouse 基线测试

# 安装 Lighthouse CLI
npm install -g lighthouse
 
# 测试生产构建
npx nuxi build && npx nuxi preview &
lighthouse http://localhost:3000 --output=json --output-path=./lighthouse-baseline.json

记录基线分数,后续每次优化后对比。

2. Bundle 优化

2.1 Tree Shaking 检查

// ❌ 导入整个库
import _ from 'lodash'
_.debounce(fn, 300)
 
// ✅ 按需导入
import { debounce } from 'lodash-es'
debounce(fn, 300)
 
// ✅ 更好:使用 VueUse 替代
import { useDebounceFn } from '@vueuse/core'
const debouncedFn = useDebounceFn(fn, 300)

2.2 手动 chunk 拆分

// nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor-ui': ['@nuxt/ui'],
            'vendor-chart': ['chart.js', 'echarts'],
            'vendor-editor': ['tiptap', 'prosemirror'],
          },
        },
      },
    },
  },
})

效果:大型第三方库独立成 chunk,首屏不需要的库不会阻塞加载。

2.3 组件懒加载

<template>
  <!-- Lazy 前缀:Nuxt 自动转为异步组件 -->
  <LazyVideoPlayer v-if="showPlayer" :src="videoUrl" />
  <LazyRichEditor v-if="isEditing" v-model="content" />
  <LazyAdminChart v-if="isAdmin" :data="chartData" />
</template>

规则:首屏不可见的组件都加 Lazy 前缀。

2.4 外部化大型依赖

// nuxt.config.ts — 把大型库从 bundle 中排除,用 CDN 加载
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        { src: 'https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js', defer: true },
      ],
    },
  },
  vite: {
    build: {
      rollupOptions: {
        external: ['echarts'],
      },
    },
  },
})

3. 渲染策略

3.1 routeRules 混合渲染

export default defineNuxtConfig({
  routeRules: {
    // 静态页面:构建时预渲染
    '/': { prerender: true },
    '/about': { prerender: true },
    '/pricing': { prerender: true },
 
    // ISR(增量静态再生):缓存 10 分钟
    '/video/**': { swr: 600 },
    '/category/**': { swr: 3600 },
 
    // SPA:管理后台不需要 SSR
    '/admin/**': { ssr: false },
 
    // API 缓存
    '/api/tags/popular': { swr: 300 },
    '/api/config': { swr: 86400 },
  },
})

效果

策略TTFBSEO适用场景
prerender最快不变的静态页
swr低频变化的内容页
SSR(默认)中等实时数据页
ssr: false管理后台

3.2 Nitro 预渲染配置

export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true,       // 自动爬取页面中的链接
      routes: ['/sitemap.xml'],
      failOnError: false,     // 某个页面失败不阻断构建
    },
  },
})

4. 缓存配置

4.1 API 路由缓存

// server/api/videos/popular.get.ts
export default defineCachedEventHandler(async () => {
  const videos = await db.select()
    .from(videosTable)
    .orderBy(desc(videosTable.viewCount))
    .limit(20)
  return videos
}, {
  maxAge: 60 * 5,      // 缓存 5 分钟
  swr: true,            // 过期后返回旧数据,后台刷新
  name: 'popular-videos',
  getKey: () => 'popular-videos',
})

4.2 客户端数据缓存

// useFetch 配合 getCachedData
const { data } = await useAsyncData('video-list', () => $fetch('/api/videos'), {
  getCachedData: (key, nuxtApp) => {
    return nuxtApp.payload.data[key] || nuxtApp.static.data[key]
  },
})

4.3 HTTP 缓存头

// server/api/static-config.get.ts
export default defineEventHandler((event) => {
  // 设置强缓存
  setResponseHeaders(event, {
    'Cache-Control': 'public, max-age=3600, s-maxage=86400',
    'CDN-Cache-Control': 'max-age=86400',
  })
  return { version: '1.0', features: [] }
})

4.4 Nitro 存储缓存

// nuxt.config.ts — 配置 Redis 作为缓存存储
export default defineNuxtConfig({
  nitro: {
    storage: {
      cache: {
        driver: 'redis',
        host: process.env.REDIS_HOST || 'localhost',
        port: 6379,
        db: 0,
      },
    },
  },
})

5. 图片与字体优化

5.1 NuxtImg 自动优化

<template>
  <!-- 自动生成 WebP/AVIF、响应式 srcset -->
  <NuxtImg
    src="/images/hero.jpg"
    width="1200"
    height="630"
    format="webp"
    quality="80"
    loading="lazy"
    sizes="sm:100vw md:50vw lg:33vw"
    placeholder
  />
</template>
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    quality: 80,
    format: ['webp', 'avif'],
    screens: {
      sm: 640,
      md: 768,
      lg: 1024,
      xl: 1280,
    },
  },
})

5.2 图片懒加载策略

<template>
  <!-- 首屏图片:eager 加载 -->
  <NuxtImg src="/hero.jpg" loading="eager" fetchpriority="high" />
 
  <!-- 非首屏图片:lazy 加载 -->
  <NuxtImg
    v-for="video in videos"
    :key="video.id"
    :src="video.thumbnail"
    loading="lazy"
  />
</template>

5.3 字体优化

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/fonts'],
  fonts: {
    defaults: {
      weights: [400, 500, 700],
      styles: ['normal'],
      subsets: ['latin', 'chinese-simplified'],
    },
  },
})

@nuxt/fonts 自动:

  • 下载字体到本地(避免第三方请求)
  • 生成 font-display: swap(避免 FOIT)
  • 按需加载字符子集

5.4 关键 CSS 内联

export default defineNuxtConfig({
  experimental: {
    inlineSSRStyles: true,  // 首屏关键 CSS 内联到 HTML
  },
})

效果:减少一次 CSS 请求,FCP 提升 100-300ms。

6. 第三方脚本延迟加载

6.1 useScript(Nuxt Scripts)

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/scripts'],
})
<script setup lang="ts">
// Google Analytics — 延迟加载,不阻塞首屏
const { proxy } = useScript('https://www.googletagmanager.com/gtag/js?id=G-XXXXX', {
  trigger: 'idle',  // 浏览器空闲时加载
})
</script>

6.2 手动延迟加载

<script setup lang="ts">
// 用户交互后才加载(如聊天组件)
const loadChat = ref(false)
 
function initChat() {
  loadChat.value = true
}
</script>
 
<template>
  <button @click="initChat">打开客服</button>
  <LazyLiveChatWidget v-if="loadChat" />
</template>

6.3 DNS 预解析

export default defineNuxtConfig({
  app: {
    head: {
      link: [
        { rel: 'dns-prefetch', href: 'https://fonts.googleapis.com' },
        { rel: 'dns-prefetch', href: 'https://cdn.example.com' },
        { rel: 'preconnect', href: 'https://api.example.com' },
      ],
    },
  },
})

7. Core Web Vitals 达标验证

7.1 指标目标

指标需改进
LCP (Largest Contentful Paint)< 2.5s2.5-4s> 4s
INP (Interaction to Next Paint)< 200ms200-500ms> 500ms
CLS (Cumulative Layout Shift)< 0.10.1-0.25> 0.25

7.2 LCP 优化清单

- [ ] 首屏大图使用 fetchpriority="high" + loading="eager"
- [ ] 首屏关键 CSS 内联(inlineSSRStyles)
- [ ] 字体使用 font-display: swap
- [ ] 服务端响应时间 < 200ms(TTFB)
- [ ] 避免 render-blocking 的第三方脚本

7.3 CLS 优化清单

- [ ] 图片/视频设置明确的 width 和 height
- [ ] 字体加载不导致文字跳动(@nuxt/fonts 自动处理)
- [ ] 动态内容区域预留空间(skeleton / min-height)
- [ ] 广告/嵌入内容使用固定容器

7.4 INP 优化清单

- [ ] 事件处理器中的耗时操作使用 requestAnimationFrame 或 Web Worker
- [ ] 长列表使用虚拟滚动(vue-virtual-scroller)
- [ ] 避免在 JS 主线程中做大量 DOM 操作
- [ ] 使用 CSS 动画替代 JS 动画

7.5 Lighthouse CI 自动化

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [pull_request]
 
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
 
      - run: npm install -g @lhci/cli
      - run: pnpm install
      - run: pnpm build
 
      - name: Run Lighthouse
        run: |
          lhci autorun --config=lighthouserc.json
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
// lighthouserc.json
{
  "ci": {
    "collect": {
      "startServerCommand": "node .output/server/index.mjs",
      "url": ["http://localhost:3000/", "http://localhost:3000/video/1"],
      "numberOfRuns": 3
    },
    "assert": {
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }],
        "categories:accessibility": ["warn", { "minScore": 0.9 }],
        "categories:seo": ["warn", { "minScore": 0.9 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

效果:每个 PR 自动运行 Lighthouse,Performance < 90 分自动阻断合并。

本章小结

性能优化 Checklist 七步走:

  • 分析nuxi analyze 看 bundle、检查 payload 大小、Lighthouse 基线
  • Bundle:Tree Shaking、手动 chunk 拆分、Lazy 前缀懒加载、CDN 外部化
  • 渲染:routeRules 混合渲染(prerender + SWR + SSR + SPA)
  • 缓存:defineCachedEventHandler 服务端缓存、getCachedData 客户端缓存、HTTP 缓存头、Redis 存储
  • 资源:NuxtImg 自动 WebP/AVIF、字体本地化 + swap、关键 CSS 内联
  • 脚本:useScript idle 加载、用户交互触发加载、DNS 预解析
  • 验证:LCP < 2.5s、CLS < 0.1、INP < 200ms、Lighthouse CI 自动化守门