本文 AI 產出,尚未審核

Vue3 路由系統(Vue Router 4)—— Lazy Loading 路由

簡介

在單頁應用(SPA)中,路由是連接不同畫面的關鍵。隨著功能日益龐大,若一次性載入所有路由對應的元件,會導致首屏載入時間過長、使用者體驗下降。
Lazy Loading(懶載入)正是為了解決這個問題:只有在使用者真的要前往某個路由時,才動態下載對應的程式碼。這不僅縮短了初始載入大小,也讓瀏覽器的快取更有效率。

在 Vue3 生態中,Vue Router 4 完全支援動態匯入(import())與 Route‑Based Code Splitting,只要稍微調整路由設定,就能輕鬆實現懶載入。本篇教學將從概念說明、實作範例到最佳實踐,帶你一步步在 Vue3 專案中導入 lazy loading。


核心概念

1. 為什麼需要懶載入

  • 縮短首屏載入時間:只載入當前畫面所需的 JavaScript,其他頁面的程式碼延後下載。
  • 降低帶寬消耗:使用者只會下載他實際瀏覽過的路由,對行動裝置尤其友好。
  • 提升效能:較小的 bundle 讓瀏覽器解析、執行速度更快,減少記憶體佔用。

2. Vue Router 4 的懶載入語法

在 Vue Router 4 中,路由的 component 屬性可以接受 動態匯入回傳的 Promise:

{
  path: '/about',
  component: () => import('@/views/About.vue')
}

當使用者導航到 /about 時,Webpack(或 Vite)會自動產生一個獨立的 chunk,只有在此時才會被下載。

3. 命名 chunk 以利除錯與快取

使用 /* webpackChunkName: "about" */(Webpack)或 /* @vite-ignore */(Vite)可以自行命名產出的檔案,方便除錯與快取策略:

{
  path: '/about',
  component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
}

4. 結合 defineAsyncComponent 的進階寫法

Vue 3 內建 defineAsyncComponent,允許設定 載入中的占位元件錯誤處理重試次數,配合路由使用可提升使用者體驗:

import { defineAsyncComponent } from 'vue'

const AsyncAbout = defineAsyncComponent({
  // 匯入元件的函式
  loader: () => import('@/views/About.vue'),
  // 載入期間顯示的元件
  loadingComponent: {
    template: '<p>載入中…</p>'
  },
  // 發生錯誤時顯示的元件
  errorComponent: {
    template: '<p>載入失敗,請稍後再試。</p>'
  },
  // 載入逾時時間(毫秒)
  timeout: 3000,
  // 重試次數
  retry: 2
})

export default [
  { path: '/about', component: AsyncAbout }
]

5. 多層嵌套路由的懶載入

在父子路由結構中,每一層都可以單獨懶載入,保持子路由僅在需要時才下載:

export default [
  {
    path: '/dashboard',
    component: () => import('@/layouts/DashboardLayout.vue'), // 父層懶載入
    children: [
      {
        path: '',
        name: 'DashboardHome',
        component: () => import('@/views/dashboard/Home.vue')
      },
      {
        path: 'settings',
        name: 'DashboardSettings',
        component: () => import('@/views/dashboard/Settings.vue')
      }
    ]
  }
]

程式碼範例

範例 1:最簡單的懶載入路由

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')   // 立即載入
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/About.vue') // **懶載入**
  }
]

export default createRouter({
  history: createWebHistory(),
  routes
})

說明Home.vue 會在應用啟動時被打包進主 bundle,About.vue 則會產生獨立 chunk,只有在使用者點擊「About」時才會下載。


範例 2:為 chunk 命名,提升快取效率

{
  path: '/profile',
  name: 'Profile',
  component: () =>
    import(
      /* webpackChunkName: "user-profile" */
      '@/views/Profile.vue'
    )
}

說明:產生的檔名會是 user-profile.[hash].js,若日後需要針對使用者頁面做 CDN 快取設定,只要針對這個名稱即可。


範例 3:使用 defineAsyncComponent 加載占位與錯誤處理

import { defineAsyncComponent } from 'vue'

const AsyncContact = defineAsyncComponent({
  loader: () => import('@/views/Contact.vue'),
  loadingComponent: {
    template: '<div class="spinner">載入中…</div>'
  },
  errorComponent: {
    template: '<div class="error">Oops! 載入失敗。</div>'
  },
  timeout: 5000,
  retry: 3
})

export default [
  { path: '/contact', component: AsyncContact }
]

說明:使用者在網路較慢時會看到自訂的 loading UI,若超過 5 秒仍未成功,會顯示錯誤訊息,並自動重試最多三次。


範例 4:懶載入嵌套路由(Dashboard)

export default [
  {
    path: '/dashboard',
    component: () => import('@/layouts/DashboardLayout.vue'), // 父層懶載入
    children: [
      {
        path: '',
        name: 'DashboardHome',
        component: () => import('@/views/dashboard/Home.vue')
      },
      {
        path: 'reports',
        name: 'DashboardReports',
        component: () => import('@/views/dashboard/Reports.vue')
      },
      {
        path: 'settings',
        name: 'DashboardSettings',
        component: () => import('@/views/dashboard/Settings.vue')
      }
    ]
  }
]

說明:即使使用者只進入 DashboardHomeReports.vueSettings.vue 仍不會被下載,減少不必要的流量。


範例 5:結合路由守衛(Navigation Guard)與懶載入

{
  path: '/admin',
  name: 'Admin',
  component: () => import('@/views/Admin.vue'),
  beforeEnter: async (to, from, next) => {
    const hasPermission = await checkUserPermission()
    if (hasPermission) {
      next()
    } else {
      next({ name: 'Forbidden' })
    }
  }
}

說明:在 beforeEnter 中先做權限驗證,只有通過後才會真正載入 Admin.vue,避免未授權使用者下載管理介面的程式碼。


常見陷阱與最佳實踐

陷阱 可能的問題 解決方式
忘記設定 webpackChunkName 產生的 chunk 名稱是隨機的 hash,難以追蹤與快取 import() 註解中加入 webpackChunkName(或 Vite 的 /* @vite-ignore */
懶載入過度細分 每個小元件都產生獨立 chunk,導致大量 HTTP 請求 依功能模組劃分,例如把同一功能的多個頁面合併成同一個 chunk
載入失敗未處理 網路不佳時使用者看到白屏 使用 defineAsyncComponenterrorComponent 或在路由守衛中捕獲錯誤
SSR(伺服器端渲染)時未考慮 動態匯入在 SSR 環境可能導致 hydration 不一致 router.isReady() 前先確保所有必要的 chunk 已預取,或使用 vite-plugin-ssrpreload 功能
懶載入的路由未在導航列預先加載 使用者點擊導航列時仍會有短暫的 loading 效果 使用 router.prefetch()(Vue Router 4.2+)於滑鼠懸停時預先下載對應 chunk

最佳實踐

  1. 按功能模組分組:例如 user/*admin/* 各自產生一個 chunk。
  2. 使用占位元件:提供視覺回饋,避免使用者感受到「卡住」的情況。
  3. 設定合理的 timeout:避免長時間等待,超時後給予重試或錯誤訊息。
  4. 預先加載關鍵路由:對於常用的頁面(如登入後的首頁),可以在應用啟動時使用 router.isReady()import() 手動觸發預載。
  5. 監控 bundle 大小:使用 webpack-bundle-analyzer 或 Vite 的 visualizer 定期檢查懶載入的效果,確保沒有意外的巨型 chunk。

實際應用場景

場景 為何適合懶載入
大型企業後台系統 功能眾多,使用者只會常用部份模組,懶載入可大幅減少首次載入時間。
行動裝置優先的電商網站 手機網路不穩,懶載入商品詳情、評論等次要頁面,可提升購物流程的流暢度。
多語系平台 每個語系的資料表或元件可能不同,透過懶載入僅在切換語系時下載對應資源。
需要頻繁更新的功能 例如公告或報表頁面,使用懶載入讓新版本的程式碼可獨立部署,不影響主應用。
SSR + CSR 混合模式 首頁使用 SSR 渲染,次要頁面使用懶載入的 CSR,兼顧 SEO 與效能。

總結

懶載入是 Vue3 + Vue Router 4 中提升單頁應用效能的關鍵技巧。透過簡單的 import() 語法,我們可以把每個路由的程式碼切割成獨立的 chunk,讓使用者只在需要時才下載。結合 defineAsyncComponent、命名 chunk、預先加載與錯誤處理,能打造出 快速、穩定且具備良好使用者體驗 的應用程式。

在實務開發中,建議先從「功能模組」的角度規劃懶載入策略,避免過度細分或過度集中,並持續使用分析工具監控 bundle 大小。只要掌握上述概念與最佳實踐,懶載入將成為提升 Vue3 應用效能的強大武器。祝你開發順利,寫出更快、更好用的 Vue3 應用!