Lazy Loading | Vue Router - Optimize Your Application Performance ⚡
Load components on demand - Reduce initial bundle size and improve user experience!
🎯 Overview
Lazy loading allows you to split your application into smaller chunks that are loaded only when needed. This significantly reduces the initial bundle size, resulting in faster page loads and better performance, especially for large applications.
📋 Key Benefits
- Faster Initial Load: Smaller initial JavaScript bundle
- Better Performance: Load code only when users need it
- Improved UX: Perceived faster application
- Bandwidth Savings: Reduced data transfer for mobile users
💡 When to Use Lazy Loading
🚀 Performance Optimization
Large Applications
- Applications with many routes and components
- Feature-rich dashboards and admin panels
- E-commerce sites with multiple product categories
Mobile-First Development
- Applications targeting mobile users
- Progressive Web Apps (PWAs)
- Bandwidth-constrained environments
🔧 Technical Implementation
// Instead of static imports
import Home from './views/Home.vue'
import About from './views/About.vue'
// Use dynamic imports for lazy loading
const Home = () => import('./views/Home.vue')
const About = () => import('./views/About.vue')🚀 Getting Started
Basic Lazy Loading Syntax
const router = createRouter({
routes: [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
},
{
path: '/contact',
component: () => import('./views/Contact.vue')
}
]
})Webpack Chunk Names
You can specify chunk names for better debugging and optimization:
{
path: '/user/:id',
component: () => import(/* webpackChunkName: "user" */ './views/User.vue')
}
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ './views/Admin.vue')
}🔧 Advanced Techniques
Grouping Related Routes
Group related routes into the same chunk for better caching:
// User-related routes in same chunk
const UserRoutes = {
profile: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue'),
settings: () => import(/* webpackChunkName: "user" */ './views/UserSettings.vue'),
preferences: () => import(/* webpackChunkName: "user" */ './views/UserPreferences.vue')
}
const router = createRouter({
routes: [
{
path: '/user/profile',
component: UserRoutes.profile
},
{
path: '/user/settings',
component: UserRoutes.settings
},
{
path: '/user/preferences',
component: UserRoutes.preferences
}
]
})Preloading Strategies
Implement different preloading strategies for optimal performance:
// Preload important routes after initial load
function preloadCriticalRoutes() {
// Preload routes that users are likely to visit next
import('./views/Dashboard.vue')
import('./views/Products.vue')
}
// Execute after main app loads
setTimeout(preloadCriticalRoutes, 2000)🛠️ Practical Examples
Example 1: E-commerce Application
const router = createRouter({
routes: [
{
path: '/',
component: () => import(/* webpackChunkName: "home" */ './views/Home.vue')
},
{
path: '/products',
component: () => import(/* webpackChunkName: "products" */ './views/ProductList.vue')
},
{
path: '/products/:id',
component: () => import(/* webpackChunkName: "product-detail" */ './views/ProductDetail.vue')
},
{
path: '/cart',
component: () => import(/* webpackChunkName: "cart" */ './views/Cart.vue')
},
{
path: '/checkout',
component: () => import(/* webpackChunkName: "checkout" */ './views/Checkout.vue')
},
{
path: '/user',
component: () => import(/* webpackChunkName: "user" */ './views/UserLayout.vue'),
children: [
{
path: 'profile',
component: () => import(/* webpackChunkName: "user" */ './views/UserProfile.vue')
},
{
path: 'orders',
component: () => import(/* webpackChunkName: "user" */ './views/UserOrders.vue')
}
]
}
]
})Example 2: Admin Dashboard
// Admin routes with role-based access
const AdminRoutes = {
dashboard: () => import(/* webpackChunkName: "admin" */ './views/admin/Dashboard.vue'),
users: () => import(/* webpackChunkName: "admin" */ './views/admin/Users.vue'),
analytics: () => import(/* webpackChunkName: "admin" */ './views/admin/Analytics.vue'),
settings: () => import(/* webpackChunkName: "admin" */ './views/admin/Settings.vue')
}
const router = createRouter({
routes: [
{
path: '/admin',
component: () => import('./views/AdminLayout.vue'),
meta: { requiresAuth: true, requiresAdmin: true },
children: [
{ path: '', component: AdminRoutes.dashboard },
{ path: 'users', component: AdminRoutes.users },
{ path: 'analytics', component: AdminRoutes.analytics },
{ path: 'settings', component: AdminRoutes.settings }
]
}
]
})🔍 Loading States and Error Handling
Loading Component
Provide feedback while components are loading:
<template>
<RouterView v-slot="{ Component }">
<Suspense>
<component :is="Component" />
<template #fallback>
<div class="loading-container">
<LoadingSpinner />
<p>Loading page...</p>
</div>
</template>
</Suspense>
</RouterView>
</template>
<script setup>
import { Suspense } from 'vue'
import LoadingSpinner from './components/LoadingSpinner.vue'
</script>
<style scoped>
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
}
</style>Error Boundary
Handle component loading errors gracefully:
<template>
<RouterView v-slot="{ Component }">
<component
:is="Component"
v-if="!hasError"
@error="handleComponentError"
/>
<ErrorFallback v-else @retry="handleRetry" />
</RouterView>
</template>
<script setup>
import { ref } from 'vue'
import ErrorFallback from './components/ErrorFallback.vue'
const hasError = ref(false)
function handleComponentError(error) {
console.error('Component loading failed:', error)
hasError.value = true
}
function handleRetry() {
hasError.value = false
// Optionally reload the route
router.go(0)
}
</script>🚀 Performance Optimization
Route-Based Code Splitting
Split your application based on user navigation patterns:
// Critical routes (load immediately)
const criticalRoutes = {
home: () => import(/* webpackChunkName: "critical" */ './views/Home.vue'),
login: () => import(/* webpackChunkName: "critical" */ './views/Login.vue')
}
// Secondary routes (load on demand)
const secondaryRoutes = {
about: () => import(/* webpackChunkName: "secondary" */ './views/About.vue'),
contact: () => import(/* webpackChunkName: "secondary" */ './views/Contact.vue')
}
// Admin routes (load only for admin users)
const adminRoutes = {
dashboard: () => import(/* webpackChunkName: "admin" */ './views/Admin.vue')
}Prefetching Strategies
Implement intelligent prefetching:
// Prefetch routes when user hovers over links
function setupRoutePrefetching() {
document.addEventListener('mouseover', (e) => {
const link = e.target.closest('[data-prefetch]')
if (link) {
const routePath = link.getAttribute('data-prefetch')
prefetchRoute(routePath)
}
})
}
async function prefetchRoute(path) {
const route = router.resolve(path)
if (route.matched.length > 0) {
// Preload the component
await route.matched[0].components.default()
}
}🔧 Vite-Specific Configuration
Vite's Dynamic Import
Vite handles dynamic imports differently than Webpack:
// Vite automatically handles code splitting
const Home = () => import('./views/Home.vue')
// You can still use comments for chunk naming
const About = () => import(/* @vite-ignore */ './views/About.vue')Manual Chunks Configuration
Configure manual chunks in vite.config.js:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router'],
utils: ['./src/utils/*'],
components: ['./src/components/*']
}
}
}
}
})🎯 Best Practices
✅ Do
- Lazy Load Large Components: Components over 10KB
- Group Related Routes: Keep similar functionality together
- Use Meaningful Chunk Names: Help with debugging
- Implement Loading States: Provide user feedback
- Handle Errors Gracefully: Prepare for network failures
❌ Don't
- Over-Split: Too many small chunks can hurt performance
- Lazy Load Critical Components: Keep essential UI in main bundle
- Ignore Mobile Users: Consider bandwidth limitations
- Forget Error Handling: Network failures happen
📚 Related Features
- Route Prefetching: Load routes before navigation
- Navigation Guards: Control access to lazy-loaded routes
- Route Meta Fields: Add metadata for loading strategies
- Webpack Bundle Analyzer: Visualize your bundle structure
💡 Pro Tip
Use the webpack-bundle-analyzer to visualize your bundle structure and identify the best candidates for lazy loading based on size and usage patterns!
Ready to optimize your application? Start implementing lazy loading to create faster, more efficient Vue applications! ⚡