Skip to main content
Vue.js for Beginners to Advanced
CHAPTER 25 Beginner

Performance Optimization in Vue

Updated: May 18, 2026
5 min read

# CHAPTER 25

Performance Optimization in Vue

1. Chapter Introduction

A fast app retains users; a slow app loses them. Vue 3 is already highly optimized, but understanding the performance tools available — lazy loading, code splitting, virtual lists, and memoization — allows you to build apps that feel instant even at scale.

2. Learning Objectives

  • Implement lazy loading for routes and components.
  • Analyze and reduce bundle size.
  • Use <KeepAlive> for component caching.
  • Avoid unnecessary re-renders with v-memo and shallowRef.
  • Implement virtual scrolling for large lists.

3. Route Lazy Loading

javascript
1234567891011121314
// ❌ Eager — ALL components downloaded on initial load
import HomeView from &#039;@/views/HomeView.vue&#039;
import AdminView from &#039;@/views/AdminView.vue&#039;

// ✅ Lazy — each component only downloaded when route is visited
const routes = [
  { path: &#039;/&#039;, component: () => import(&#039;@/views/HomeView.vue&#039;) },
  { path: &#039;/admin&#039;, component: () => import(&#039;@/views/AdminView.vue&#039;) },
  // Group chunks — AdminView and AdminUsers in same chunk
  {
    path: &#039;/admin/users&#039;,
    component: () => import(/* webpackChunkName: "admin" */ &#039;@/views/AdminUsers.vue&#039;)
  }
]

4. Async Components

vue
1234567891011121314151617181920212223242526272829
<script setup>
import { defineAsyncComponent } from &#039;vue'

// Lazy load heavy components
const HeavyChart = defineAsyncComponent(() =>
  import(&#039;@/components/HeavyChart.vue')
)

// With loading and error states
const HeavyTable = defineAsyncComponent({
  loader: () => import(&#039;@/components/HeavyTable.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorMessage,
  delay: 200,     // Show loading after 200ms
  timeout: 10000  // Show error after 10s
})
</script>

<template>
  <!-- Use Suspense for async component loading -->
  <Suspense>
    <template #default>
      <HeavyChart />
    </template>
    <template #fallback>
      <div>Loading chart...</div>
    </template>
  </Suspense>
</template>

5. KeepAlive — Component Caching

vue
123456789101112
<template>
  <!-- Without KeepAlive: component destroys/recreates on every tab switch -->
  <!-- With KeepAlive: component state preserved in memory -->
  <KeepAlive :include="[&#039;HomeView', 'ProductsView']" :max="5">
    <RouterView />
  </KeepAlive>

  <!-- Tab component that stays alive -->
  <KeepAlive>
    <component :is="currentTab === &#039;posts' ? PostsList : UsersList" />
  </KeepAlive>
</template>

6. v-memo — Prevent Re-renders

vue
123456789101112
<template>
  <!-- v-memo: skip re-render if listed deps haven&#039;t changed -->
  <!-- Perfect for large v-for lists with complex items -->
  <div
    v-for="item in largeList"
    :key="item.id"
    v-memo="[item.id, item.selected, item.status]"
  >
    <!-- This subtree only re-renders if id, selected, or status changes -->
    <ComplexItemCard :item="item" />
  </div>
</template>

7. shallowRef and shallowReactive

vue
1234567891011121314151617181920
<script setup>
import { ref, shallowRef, shallowReactive } from &#039;vue'

// ref: deep reactive — watches ALL nested properties
const deepData = ref({ user: { profile: { avatar: &#039;...' } } })

// shallowRef: only reactive at top level
// Changing deepData.value.user.name does NOT trigger update
// But replacing deepData.value = {} DOES trigger update
const shallowData = shallowRef({ user: { profile: { avatar: &#039;...' } } })

// Use shallowRef for large data sets where you only replace entire value
const bigList = shallowRef([/* 10,000 items */])

// Force update after mutation
function updateBigList() {
  bigList.value.push(newItem)
  triggerRef(bigList)  // Manually trigger since shallowRef won't auto-detect
}
</script>

8. Virtual Scrolling for Large Lists

bash
1
npm install vue-virtual-scroller
vue
123456789101112131415161718192021222324252627282930313233
<script setup>
import { ref } from &#039;vue'
import { RecycleScroller } from &#039;vue-virtual-scroller'
import &#039;vue-virtual-scroller/dist/vue-virtual-scroller.css'

// 100,000 items — normal v-for would freeze the browser
const items = ref(Array.from({ length: 100000 }, (_, i) => ({
  id: i + 1,
  name: `User ${i + 1}`,
  email: `user${i + 1}@example.com`
})))
</script>

<template>
  <!-- Only renders visible items (typically ~20) regardless of list size -->
  <RecycleScroller
    class="scroller"
    :items="items"
    :item-size="60"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="list-item">
      <strong>{{ item.name }}</strong>
      <span>{{ item.email }}</span>
    </div>
  </RecycleScroller>
</template>

<style>
.scroller { height: 600px; }
.list-item { height: 60px; display: flex; align-items: center; gap: 1rem; padding: 0 1rem; border-bottom: 1px solid #f1f5f9; }
</style>

9. Common Mistakes

  • Deep reactivity on large datasets: Using ref([...10000items]) makes every nested property reactive. Use shallowRef for large arrays.
  • No KeepAlive on frequently toggled components: Forms and lists that re-mount constantly lose state and re-fetch data. <KeepAlive> solves this.

10. MCQs

Question 1

Lazy route loading uses?

Question 2

<KeepAlive> prevents?

Question 3

v-memo skips re-render when?

Question 4

shallowRef is deep reactive?

Question 5

Virtual scrolling renders?

Question 6

defineAsyncComponent is for?

Question 7

<Suspense> is for?

Question 8

Bundle size analysis tool for Vite?

Question 9

onActivated hook fires when?

Question 10

computed caching benefit?

11. Interview Questions

  • Q: What is virtual scrolling and when should you use it?
  • Q: How does <KeepAlive> improve Vue app performance?

12. Summary

Vue 3 performance optimization is layered: lazy-load routes and components, <KeepAlive> for expensive components, v-memo for large v-for lists, shallowRef for big data, and virtual scrolling for lists of thousands. Each tool addresses a specific bottleneck.

13. Next Chapter Recommendation

In Chapter 26: Testing in Vue.js, we write unit tests for components, composables, and stores using Vitest and Vue Test Utils.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·