Skip to content

Lazy Loading

12.3.1. Route-Level Code Splitting

  • All route components must be lazy-loaded using dynamic import().
  • Static import of route components is not permitted.
  • When dynamic import() is used, Vite automatically generates per-route chunks.
typescript
// Correct: dynamic import
{
  path: '/dashboard',
  name: 'Dashboard',
  component: () => import('@/views/DashboardView.vue'),
  meta: { title: 'Dashboard', requiresAuth: true },
}

// Incorrect: static import (prohibited)
import DashboardView from '@/views/DashboardView.vue'
{
  path: '/dashboard',
  name: 'Dashboard',
  component: DashboardView,
}
  • As an exception, top-level layout components that are always rendered may use static import.

12.3.2. Loading State Handling

  • A loading state must be displayed to the user during lazy loading.
  • defineAsyncComponent must be used to specify loading and error components.
  • The delay option should be set to prevent a loading flash when network latency is short.
typescript
import { defineAsyncComponent } from 'vue'
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
import ErrorFallback from '@/components/common/ErrorFallback.vue'

const AsyncDashboard = defineAsyncComponent({
  loader: () => import('@/views/DashboardView.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorFallback,
  delay: 200,
  timeout: 10000,
})
OptionDescriptionDefault
loaderFunction that returns the componentRequired
loadingComponentComponent displayed during loadingNone
errorComponentComponent displayed on load failureNone
delayDelay before showing the loading component (ms)200
timeoutTimeout duration (ms)Unlimited

12.3.3. Preload Strategy

  • To improve user experience, a strategy for preloading the next page's chunks should be applied.
  • Choose one of the following 3 preload methods based on project characteristics.

Preload on Link Hover

vue
<script setup lang="ts">
function preloadRoute(importFn: () => Promise<unknown>) {
  importFn()
}
</script>

<template>
  <RouterLink
    :to="{ name: 'UserList' }"
    @mouseenter="preloadRoute(() => import('@/views/user/UserListView.vue'))"
  >
    User List
  </RouterLink>
</template>

Preload on Viewport Entry

typescript
// src/composables/useRoutePreload.ts
import { useIntersectionObserver } from '@vueuse/core'
import type { Ref } from 'vue'

export function useRoutePreload(
  target: Ref<HTMLElement | null>,
  importFn: () => Promise<unknown>
) {
  let loaded = false

  useIntersectionObserver(target, ([entry]) => {
    if (entry.isIntersecting && !loaded) {
      loaded = true
      importFn()
    }
  })
}

HTML Link Tag Prefetch

  • Vite automatically injects <link rel="modulepreload"> during the build process.
  • For additional prefetching, use plugins such as vite-plugin-preload.

12.3.4. Chunk Naming

  • In a Vite environment, rollupOptions.output.chunkFileNames must be used to configure chunk naming patterns.
  • Meaningful chunk names must be assigned for debugging and bundle analysis.
typescript
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: 'assets/[name]-[hash].[ext]',
        manualChunks: {
          'vendor-vue': ['vue', 'vue-router', 'pinia'],
        },
      },
    },
  },
})
ItemRule
Route componentsDynamic import() required
Loading componentSpecified via defineAsyncComponent
PreloadingApply hover or viewport entry method
Chunk namingConfigure pattern via rollupOptions
Vendor separationSeparate common libraries via manualChunks

TIENIPIA QUALIFIED STANDARD