Chuyển đến nội dung chính

レッスン 20: パフォーマンスの最適化

バンドル分析、ツリーシェイク。コード分​​割、コンポーネントの遅延読み込み。ペイロードの最適化、キャッシュ戦略。 Vue DevTools のパフォーマンス タブ。ライトハウス、コアウェブバイタル。

💻 プログラミング — レッスン 20 レッスン 20: パフォーマンスの最適化

Vue.js と Nuxt: 基本から高度まで

パート 6: テスト、展開、実稼働

xdev.asia

1. バンドル分析

// nuxt.config.ts
export default defineNuxtConfig({
  build: {
    analyze: true, // Bật webpack-bundle-analyzer
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor-ui': ['@headlessui/vue', '@heroicons/vue'],
            'vendor-charts': ['chart.js', 'vue-chartjs'],
          },
        },
      },
    },
  },
})
# Phân tích bundle
npx nuxi analyze

# Kiểm tra bundle size
npx nuxi build --analyze

2. コンポーネントの遅延読み込み

<script setup>
// Lazy load component — tự động code-split
const HeavyChart = defineAsyncComponent(() =>
  import('~/components/HeavyChart.vue')
)

// Nuxt auto-import: prefix "Lazy"
// <LazyHeavyChart /> sẽ tự code-split
</script>

<template>
  <div>
    <!-- Chỉ load khi visible -->
    <LazyHeavyChart v-if="showChart" />

    <!-- Lazy load với Suspense -->
    <Suspense>
      <template #default>
        <HeavyChart :data="chartData" />
      </template>
      <template #fallback>
        <div class="skeleton h-64"></div>
      </template>
    </Suspense>
  </div>
</template>

3. 画像の最適化

<template>
  <!-- Nuxt Image — tự động optimize -->
  <NuxtImg
    src="/images/hero.jpg"
    width="800"
    height="400"
    format="webp"
    quality="80"
    loading="lazy"
    placeholder
    sizes="sm:100vw md:50vw lg:800px"
  />

  <!-- NuxtPicture — responsive format -->
  <NuxtPicture
    src="/images/banner.jpg"
    format="avif,webp"
    :img-attrs="{ class: 'rounded-lg' }"
  />
</template>

4. ペイロードの最適化

// Giảm payload size khi SSR
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: true, // Tách payload riêng file
  },
})

// composables/useOptimizedFetch.ts
export function useOptimizedFetch<T>(url: string) {
  return useFetch<T>(url, {
    // Chỉ pick fields cần thiết
    transform: (data: any[]) => data.map(({ id, title, slug }) => ({
      id, title, slug,
    })),
    // Cache key để deduplicate
    key: url,
  })
}

5. キャッシュ戦略

// server/api/products.get.ts — Route caching
export default defineCachedEventHandler(async (event) => {
  const products = await db.select().from(productsTable)
  return products
}, {
  maxAge: 60 * 10,      // Cache 10 phút
  staleMaxAge: 60 * 60, // Stale 1 giờ
  swr: true,            // Stale-while-revalidate
  name: 'products-list',
  getKey: (event) => getQuery(event).category as string || 'all',
})

// nuxt.config.ts — ISR
export default defineNuxtConfig({
  routeRules: {
    '/': { isr: 60 },
    '/blog/**': { isr: 3600 },
    '/api/**': { cors: true, headers: { 'cache-control': 'max-age=60' } },
    '/admin/**': { ssr: false },
  },
})

6. 主要なウェブの重要事項

メトリック良い最適なテクニック
LCP< 2.5秒ヒーロー画像、SSR、フォント表示のプリロード: 入れ替え
INP< 200ms入力のデバウンス、Web ワーカー、リストの仮想化
CLS< 0.1画像、スケルトンUIの幅/高さを設定します
// plugins/web-vitals.client.ts
import { onCLS, onINP, onLCP } from 'web-vitals'

export default defineNuxtPlugin(() => {
  onLCP(sendToAnalytics)
  onINP(sendToAnalytics)
  onCLS(sendToAnalytics)
})

function sendToAnalytics(metric: any) {
  navigator.sendBeacon('/api/analytics/vitals', JSON.stringify({
    name: metric.name,
    value: metric.value,
    id: metric.id,
  }))
}

7. 仮想スクロール

<script setup>
import { useVirtualList } from '@vueuse/core'

const allItems = ref(Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  title: `Item ${i}`,
})))

const { list, containerProps, wrapperProps } = useVirtualList(allItems, {
  itemHeight: 48,
  overscan: 10,
})
</script>

<template>
  <div v-bind="containerProps" class="h-96 overflow-auto">
    <div v-bind="wrapperProps">
      <div v-for="{ data } in list" :key="data.id" class="h-12 px-4 flex items-center">
        {{ data.title }}
      </div>
    </div>
  </div>
</template>

次の記事: 導入とホスティング — Vercel、Cloudflare、Docker、CI/CD。