Lazy Loading
12.3.1. Route-Level Code Splitting
- All route components must be lazy-loaded using dynamic
import(). - Static
importof 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.
defineAsyncComponentmust be used to specify loading and error components.- The
delayoption 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,
})| Option | Description | Default |
|---|---|---|
loader | Function that returns the component | Required |
loadingComponent | Component displayed during loading | None |
errorComponent | Component displayed on load failure | None |
delay | Delay before showing the loading component (ms) | 200 |
timeout | Timeout 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.chunkFileNamesmust 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'],
},
},
},
},
})| Item | Rule |
|---|---|
| Route components | Dynamic import() required |
| Loading component | Specified via defineAsyncComponent |
| Preloading | Apply hover or viewport entry method |
| Chunk naming | Configure pattern via rollupOptions |
| Vendor separation | Separate common libraries via manualChunks |