Vue Performance: Computed State, Watchers and Component Rendering
9 Aug 2026 · 7 min read · Intermediate
- #Vue
- #Performance
Share
Vue’s reactivity is efficient by default. Most slowdowns come from over-watching, unstable keys, and doing work in templates that belongs in computed.
Computed over methods in templates
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{ items: { price: number }[] }>();
const total = computed(() =>
props.items.reduce((sum, item) => sum + item.price, 0),
);
</script>
<template>
<p>{{ total }}</p>
</template>computed caches until dependencies change. A method recalculates every render.
Watchers: intentional, not ambient
Use watch / watchEffect for side effects (syncing URL, writing storage). Do not use watchers to derive state that computed can express.
import { watch } from 'vue';
watch(
query,
async (value) => {
await router.replace({ query: { q: value || undefined } });
},
{ flush: 'post' },
);Props and v-once / v-memo
For large static subtrees, v-once helps. For conditional memoization of list children, v-memo can skip updates when dependencies are unchanged—measure before sprinkling it everywhere.
List keys
<li v-for="item in items" :key="item.id">
{{ item.label }}
</li>Index keys are a common source of subtle DOM reuse bugs when lists reorder.
Async components
Lazy-load heavy panels:
import { defineAsyncComponent } from 'vue';
const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue'));Conclusion
Vue performance is mostly about keeping reactivity graphs small and side effects deliberate. Optimize the hot path you measured—not every ref.