Skip to content

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

javascript
// 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

javascript
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:

javascript
{
  path: '/user/:id',
  component: () => import(/* webpackChunkName: "user" */ './views/User.vue')
}

{
  path: '/admin',
  component: () => import(/* webpackChunkName: "admin" */ './views/Admin.vue')
}

🔧 Advanced Techniques

Group related routes into the same chunk for better caching:

javascript
// 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:

javascript
// 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

javascript
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

javascript
// 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:

vue
<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:

vue
<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:

javascript
// 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:

javascript
// 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:

javascript
// 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:

javascript
// 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
  • 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! ⚡

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择