Clement Yuen
← Back to articles
Vue·Intermediate

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

Vue
<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.

Typescript
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

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

Typescript
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.

Related Articles