Nuxt.js性能优化 #
一、性能指标 #
1.1 核心指标 #
| 指标 | 说明 | 目标值 |
|---|---|---|
| LCP | 最大内容绘制 | < 2.5s |
| FID | 首次输入延迟 | < 100ms |
| CLS | 累积布局偏移 | < 0.1 |
| TTFB | 首字节时间 | < 600ms |
| TTI | 可交互时间 | < 3.8s |
1.2 性能分析工具 #
- Chrome DevTools
- Lighthouse
- WebPageTest
- Nuxt DevTools
二、代码分割 #
2.1 路由级分割 #
Nuxt 自动按页面分割代码。
2.2 组件懒加载 #
vue
<template>
<LazyHeavyComponent v-if="show" />
</template>
2.3 动态导入 #
vue
<script setup lang="ts">
const HeavyLibrary = defineAsyncComponent(() =>
import('~/components/HeavyLibrary.vue')
)
</script>
2.4 手动分包 #
nuxt.config.ts:
typescript
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'vue-router', 'pinia'],
'ui': ['@vueuse/core']
}
}
}
}
}
})
三、图片优化 #
3.1 使用Nuxt Image #
bash
pnpm add @nuxt/image
nuxt.config.ts:
typescript
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
quality: 80,
format: ['webp', 'avif'],
screens: {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280
}
}
})
3.2 使用优化图片 #
vue
<template>
<NuxtImg
src="/image.jpg"
width="600"
height="400"
format="webp"
loading="lazy"
placeholder
/>
</template>
3.3 响应式图片 #
vue
<template>
<NuxtPicture
src="/hero.jpg"
width="1200"
height="600"
:img-attrs="{ class: 'hero-image' }"
/>
</template>
3.4 图片预加载 #
vue
<script setup lang="ts">
useHead({
link: [
{ rel: 'preload', as: 'image', href: '/hero.webp' }
]
})
</script>
四、字体优化 #
4.1 字体预加载 #
typescript
export default defineNuxtConfig({
app: {
head: {
link: [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: true },
{ rel: 'preload', as: 'style', href: 'https://fonts.googleapis.com/css2?family=Inter&display=swap' }
]
}
}
})
4.2 本地字体 #
css
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
4.3 字体显示策略 #
css
font-display: swap;
五、缓存策略 #
5.1 路由缓存 #
typescript
export default defineNuxtConfig({
routeRules: {
'/': { isr: 3600 },
'/blog/**': { isr: 86400 },
'/api/**': { cache: { maxAge: 60 } }
}
})
5.2 静态资源缓存 #
typescript
export default defineNuxtConfig({
nitro: {
compressPublicAssets: true
}
})
5.3 API缓存 #
typescript
export default defineCachedEventHandler(
async () => {
return await fetchData()
},
{
maxAge: 60 * 60,
swr: true
}
)
六、预加载与预取 #
6.1 链接预取 #
vue
<template>
<NuxtLink to="/about" prefetch>关于</NuxtLink>
</template>
6.2 全局预取配置 #
typescript
export default defineNuxtConfig({
router: {
options: {
prefetchLinks: true
}
}
})
6.3 手动预取 #
vue
<script setup lang="ts">
const { prefetch } = useRouter()
onMounted(() => {
prefetch('/important-page')
})
</script>
6.4 预加载关键资源 #
vue
<script setup lang="ts">
useHead({
link: [
{ rel: 'preload', as: 'script', href: '/critical.js' },
{ rel: 'preload', as: 'style', href: '/critical.css' }
]
})
</script>
七、服务端优化 #
7.1 流式渲染 #
vue
<script setup lang="ts">
const { data } = await useFetch('/api/data', {
lazy: true
})
</script>
7.2 并行数据获取 #
vue
<script setup lang="ts">
const [users, posts] = await Promise.all([
useFetch('/api/users'),
useFetch('/api/posts')
])
</script>
7.3 服务端缓存 #
typescript
export default defineNuxtConfig({
nitro: {
storage: {
cache: {
driver: 'redis',
url: process.env.REDIS_URL
}
}
}
})
八、客户端优化 #
8.1 防抖与节流 #
vue
<script setup lang="ts">
import { useDebounceFn, useThrottleFn } from '@vueuse/core'
const debouncedSearch = useDebounceFn(search, 300)
const throttledScroll = useThrottleFn(handleScroll, 100)
</script>
8.2 虚拟列表 #
bash
pnpm add @vueuse/core
vue
<script setup lang="ts">
import { useVirtualList } from '@vueuse/core'
const { list, containerProps, wrapperProps } = useVirtualList(
largeList,
{ itemHeight: 50 }
)
</script>
<template>
<div v-bind="containerProps" style="height: 400px; overflow: auto;">
<div v-bind="wrapperProps">
<div v-for="{ data, index } in list" :key="index" style="height: 50px;">
{{ data.name }}
</div>
</div>
</div>
</template>
8.3 骨架屏 #
vue
<template>
<div v-if="pending" class="skeleton">
<div class="skeleton-title" />
<div class="skeleton-text" />
<div class="skeleton-text" />
</div>
<div v-else>
<h1>{{ data.title }}</h1>
<p>{{ data.content }}</p>
</div>
</template>
<style scoped>
.skeleton-title {
height: 24px;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton 1.5s infinite;
}
.skeleton-text {
height: 16px;
margin-top: 8px;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton 1.5s infinite;
}
@keyframes skeleton {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
</style>
九、打包优化 #
9.1 分析打包体积 #
bash
npx nuxi analyze
9.2 移除未使用的代码 #
typescript
export default defineNuxtConfig({
vite: {
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}
}
})
9.3 压缩配置 #
typescript
export default defineNuxtConfig({
nitro: {
compressPublicAssets: true
}
})
十、监控与分析 #
10.1 性能监控 #
vue
<script setup lang="ts">
onMounted(() => {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.duration)
}
})
observer.observe({ entryTypes: ['measure', 'navigation'] })
}
})
</script>
10.2 错误追踪 #
typescript
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:error', (error) => {
console.error('Application error:', error)
})
})
10.3 Web Vitals #
typescript
export default defineNuxtPlugin(() => {
if (import.meta.client) {
const reportWebVitals = (metric: any) => {
console.log(metric)
}
import('web-vitals').then(({ getCLS, getFID, getLCP }) => {
getCLS(reportWebVitals)
getFID(reportWebVitals)
getLCP(reportWebVitals)
})
}
})
十一、最佳实践总结 #
11.1 首屏优化 #
- 使用 SSR 或 SSG
- 预加载关键资源
- 优化字体加载
- 使用骨架屏
11.2 运行时优化 #
- 懒加载非关键组件
- 使用虚拟列表
- 合理使用缓存
- 防抖和节流
11.3 打包优化 #
- 代码分割
- Tree shaking
- 压缩资源
- 分析打包体积
十二、总结 #
本章介绍了 Nuxt.js 性能优化:
- 核心性能指标
- 代码分割策略
- 图片和字体优化
- 缓存策略配置
- 预加载与预取
- 服务端和客户端优化
- 打包优化
- 性能监控
性能优化是一个持续的过程,需要不断测量和改进。
最后更新:2026-03-28