Always use a unique, stable key when using v-for. Don’t use the index as the key if the list order can change, as this can lead to rendering issues and state bugs.
These methods mutate the original array and trigger updates:
<script setup>import { ref } from 'vue'const items = ref([1, 2, 3, 4, 5])// All of these trigger reactivityitems.value.push(6)items.value.pop()items.value.shift()items.value.unshift(0)items.value.splice(2, 1)items.value.sort()items.value.reverse()</script>
From packages/reactivity/src/arrayInstrumentations.ts, Vue intercepts these array methods to track dependencies and trigger updates.
Vue’s reactivity system efficiently handles array replacements. You don’t need to worry about performance - Vue will reuse existing elements where possible.
Internally, Vue uses the renderList helper from packages/runtime-core/src/helpers/renderList.ts to implement v-for:
export function renderList( source: any, renderItem: (...args: any[]) => VNode): VNode[] { let ret: VNode[] if (isArray(source) || isString(source)) { ret = new Array(source.length) for (let i = 0, l = source.length; i < l; i++) { ret[i] = renderItem(source[i], i, undefined, undefined) } } else if (typeof source === 'number') { ret = new Array(source) for (let i = 0; i < source; i++) { ret[i] = renderItem(i + 1, i, undefined, undefined) } } else if (isObject(source)) { if (source[Symbol.iterator]) { ret = Array.from(source as Iterable<any>, renderItem) } else { const keys = Object.keys(source) ret = new Array(keys.length) for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] ret[i] = renderItem(source[key], key, i, undefined) } } } else { ret = [] } return ret}
Filter and sort data using computed properties rather than in the template.
<!-- Bad: Filtering in template --><li v-for="item in items.filter(i => i.active)" :key="item.id"><!-- Good: Filtering in computed --><li v-for="item in activeItems" :key="item.id">
2
Avoid expensive operations in v-for
Don’t call functions that perform expensive operations inside v-for loops.
<!-- Bad: Expensive function called for each item --><li v-for="item in items" :key="item.id"> {{ expensiveFormat(item) }}</li><!-- Good: Pre-compute values --><li v-for="item in formattedItems" :key="item.id"> {{ item.formatted }}</li>
3
Use stable keys
Always use unique, stable keys. Avoid using array indices as keys for dynamic lists.
<!-- Bad: Using index as key --><li v-for="(item, index) in items" :key="index"><!-- Good: Using stable ID --><li v-for="item in items" :key="item.id">