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>