Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce Vue 3 best practices—Composition API, script setup, TypeScript, component boundaries, and reactivity patterns
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/perf-avoid-component-abstraction-in-lists.md
1---2title: Avoid Excessive Component Abstraction in Large Lists3impact: MEDIUM4impactDescription: Each component instance has memory and render overhead - abstractions multiply this in lists5type: efficiency6tags: [vue3, performance, components, abstraction, lists, optimization]7---89# Avoid Excessive Component Abstraction in Large Lists1011**Impact: MEDIUM** - Component instances are more expensive than plain DOM nodes. While abstractions improve code organization, unnecessary nesting creates overhead. In large lists, this overhead multiplies - 100 items with 3 levels of abstraction means 300+ component instances instead of 100.1213Don't avoid abstraction entirely, but be mindful of component depth in frequently-rendered elements like list items.1415## Task List1617- Review list item components for unnecessary wrapper components18- Consider flattening component hierarchies in hot paths19- Use native elements when a component adds no value20- Profile component counts using Vue DevTools21- Focus optimization efforts on the most-rendered components2223**BAD:**24```vue25<!-- BAD: Deep abstraction in list items -->26<template>27<div class="user-list">28<!-- For 100 users: Creates 400 component instances -->29<UserCard v-for="user in users" :key="user.id" :user="user" />30</div>31</template>3233<!-- UserCard.vue -->34<template>35<Card> <!-- Wrapper component #1 -->36<CardHeader> <!-- Wrapper component #2 -->37<UserAvatar :src="user.avatar" /> <!-- Wrapper component #3 -->38</CardHeader>39<CardBody> <!-- Wrapper component #4 -->40<Text>{{ user.name }}</Text>41</CardBody>42</Card>43</template>4445<!-- Each UserCard creates: Card + CardHeader + CardBody + UserAvatar + Text46100 users = 500+ component instances -->47```4849**GOOD:**50```vue51<!-- GOOD: Flattened structure in list items -->52<template>53<div class="user-list">54<!-- For 100 users: Creates 100 component instances -->55<UserCard v-for="user in users" :key="user.id" :user="user" />56</div>57</template>5859<!-- UserCard.vue - Flattened, uses native elements -->60<template>61<div class="card">62<div class="card-header">63<img :src="user.avatar" :alt="user.name" class="avatar" />64</div>65<div class="card-body">66<span class="user-name">{{ user.name }}</span>67</div>68</div>69</template>7071<script setup>72defineProps({73user: Object74})75</script>7677<style scoped>78/* Styles that would have been in Card, CardHeader, etc. */79.card { /* ... */ }80.card-header { /* ... */ }81.card-body { /* ... */ }82.avatar { /* ... */ }83</style>84```8586## When Abstraction Is Still Worth It8788```vue89<!-- Component abstraction is valuable when: -->9091<!-- 1. Complex behavior is encapsulated -->92<UserStatusIndicator :user="user" /> <!-- Has logic, tooltips, etc. -->9394<!-- 2. Reused outside of the hot path -->95<Card> <!-- OK to use in one-off places, not in 100-item lists -->9697<!-- 3. The list itself is small -->98<template v-if="items.length < 20">99<FancyItem v-for="item in items" :key="item.id" />100</template>101102<!-- 4. Virtualization is used (only ~20 items rendered at once) -->103<RecycleScroller :items="items">104<template #default="{ item }">105<ComplexItem :item="item" /> <!-- OK - only 20 instances exist -->106</template>107</RecycleScroller>108```109110## Measuring Component Overhead111112```javascript113// In development, profile component counts114import { onMounted, getCurrentInstance } from 'vue'115116onMounted(() => {117const instance = getCurrentInstance()118let count = 0119120function countComponents(vnode) {121if (vnode.component) count++122if (vnode.children) {123vnode.children.forEach(child => {124if (child.component || child.children) countComponents(child)125})126}127}128129// Use Vue DevTools instead for accurate counts130console.log('Check Vue DevTools Components tab for instance counts')131})132```133134## Alternatives to Wrapper Components135136```vue137<!-- Instead of a <Button> component for styling: -->138<button class="btn btn-primary">Click</button>139140<!-- Instead of a <Text> component: -->141<span class="text-body">{{ content }}</span>142143<!-- Instead of layout wrapper components in lists: -->144<div class="flex items-center gap-2">145<!-- content -->146</div>147148<!-- Use CSS classes or Tailwind instead of component abstractions for styling -->149```150151## Impact Calculation152153| List Size | Components per Item | Total Instances | Memory Impact |154|-----------|---------------------|-----------------|---------------|155| 100 items | 1 (flat) | 100 | Baseline |156| 100 items | 3 (nested) | 300 | ~3x memory |157| 100 items | 5 (deeply nested) | 500 | ~5x memory |158| 1000 items | 1 (flat) | 1000 | High |159| 1000 items | 5 (deeply nested) | 5000 | Very High |160