Vue Performance Optimization

Build faster and more efficient Vue applications

⚡ Performance Optimization

Vue performance optimization involves techniques to make your applications faster and more responsive. Learn to reduce bundle size, optimize rendering, implement lazy loading, and use efficient data structures for better user experience.


<!-- Optimized component example -->
<script setup>
const items = shallowRef([]) // Shallow reactivity
const count = computed(() => items.value.length)
</script>
                                    

Key Optimization Techniques

📦

Lazy Loading

Load components when needed

const Modal = defineAsyncComponent(
  () => import('./Modal.vue')
)
🎯

Virtual Scrolling

Render only visible items

<RecycleScroller
  :items="items"
  :item-size="50"
/>
💾

Memoization

Cache expensive computations

const result = computed(() => {
  return expensiveOperation()
})
🔄

Shallow Reactivity

Optimize large data structures

const data = shallowRef({
  items: largeArray
})

🔹 Component Lazy Loading

Load components only when they're needed:

<script setup>
// Lazy load heavy components
const HeavyChart = defineAsyncComponent(() => 
  import('./components/HeavyChart.vue')
)

const HeavyTable = defineAsyncComponent(() => 
  import('./components/HeavyTable.vue')
)

const showChart = ref(false)
</script>

<template>
  <div>
    <button @click="showChart = true">Load Chart</button>
    
    <!-- Only loads when showChart is true -->
    <Suspense v-if="showChart">
      <HeavyChart />
      <template #fallback>
        <div>Loading chart...</div>
      </template>
    </Suspense>
  </div>
</template>

🔹 Computed vs Methods

Use computed properties for better performance:

<script setup>
const items = ref([1, 2, 3, 4, 5])

// ❌ BAD: Method called on every render
function getTotal() {
  console.log('Calculating total...')
  return items.value.reduce((sum, item) => sum + item, 0)
}

// ✅ GOOD: Computed cached until items change
const total = computed(() => {
  console.log('Calculating total...')
  return items.value.reduce((sum, item) => sum + item, 0)
})
</script>

<template>
  <div>
    <!-- getTotal() runs every render -->
    <p>Method: {{ getTotal() }}</p>
    
    <!-- total only recalculates when items change -->
    <p>Computed: {{ total }}</p>
  </div>
</template>

🔹 v-show vs v-if

Choose the right conditional rendering:

<script setup>
const isVisible = ref(true)
const shouldRender = ref(true)
</script>

<template>
  <div>
    <!-- ✅ Use v-show for frequent toggles -->
    <!-- Element stays in DOM, just hidden -->
    <div v-show="isVisible">
      <ExpensiveComponent />
    </div>
    
    <!-- ✅ Use v-if for rare toggles -->
    <!-- Element removed from DOM -->
    <div v-if="shouldRender">
      <HeavyComponent />
    </div>
  </div>
</template>
  • v-show: Better for frequent toggles (CSS display)
  • v-if: Better for rare toggles (DOM manipulation)

🔹 List Rendering Optimization

Optimize large lists with proper keys:

<script setup>
const users = ref([
  { id: 1, name: 'John', age: 25 },
  { id: 2, name: 'Jane', age: 30 },
  { id: 3, name: 'Bob', age: 35 }
])

// ❌ BAD: Avoid index as key
// ✅ GOOD: Use unique id as key
</script>

<template>
  <div>
    <!-- ✅ Proper key usage -->
    <div 
      v-for="user in users" 
      :key="user.id"
    >
      {{ user.name }} - {{ user.age }}
    </div>
    
    <!-- ❌ Avoid this -->
    <div 
      v-for="(user, index) in users" 
      :key="index"
    >
      {{ user.name }}
    </div>
  </div>
</template>

🔹 Shallow Reactivity

Use shallow refs for large data structures:

<script setup>
// ❌ Deep reactivity (slower for large arrays)
const deepData = ref({
  items: Array(10000).fill({ name: 'Item', value: 0 })
})

// ✅ Shallow reactivity (faster)
const shallowData = shallowRef({
  items: Array(10000).fill({ name: 'Item', value: 0 })
})

// Update shallow data
function updateShallow() {
  // Trigger update by replacing entire object
  shallowData.value = {
    items: [...shallowData.value.items, { name: 'New', value: 1 }]
  }
}
</script>

Use shallowRef when: Working with large arrays/objects that don't need deep reactivity

🔹 Debouncing and Throttling

Limit expensive operations:

<script setup>
import { useDebounceFn, useThrottleFn } from '@vueuse/core'

const searchQuery = ref('')
const results = ref([])

// Debounce: Wait for user to stop typing
const debouncedSearch = useDebounceFn(async (query) => {
  results.value = await fetch(`/api/search?q=${query}`)
    .then(r => r.json())
}, 500)

// Throttle: Limit scroll handler calls
const handleScroll = useThrottleFn(() => {
  console.log('Scroll position:', window.scrollY)
}, 200)

watch(searchQuery, (newQuery) => {
  debouncedSearch(newQuery)
})
</script>

<template>
  <input 
    v-model="searchQuery" 
    placeholder="Search..."
  />
</template>

🧠 Test Your Knowledge

When should you use computed properties instead of methods?