🌸
💫
Skip to content

Best Practices

Each component should only handle one functionality.

<!-- Bad: Multiple responsibilities -->
<template>
<div>
<user-profile />
<order-list />
<statistics-chart />
</div>
</template>
<!-- Good: Split into components -->
<template>
<div>
<UserProfile />
<OrderList />
<StatisticsChart />
</div>
</template>
<!-- Parent Component -->
<template>
<ChildComponent
:data="parentData"
@update="handleUpdate"
/>
</template>
<!-- Child Component -->
<script setup>
const props = defineProps(['data'])
const emit = defineEmits(['update'])
const sendToParent = () => {
emit('update', newData)
}
</script>

Render once and skip updates.

<template>
<div v-once>
<!-- Content that doesn't need updates -->
<h1>{{ staticTitle }}</h1>
</div>
</template>

Conditional caching.

<template>
<div v-memo="[valueA, valueB]">
<!-- Only re-render when valueA or valueB changes -->
<p>{{ valueA }} - {{ valueB }}</p>
</div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
<template>
<HeavyComponent />
</template>
src/
├── components/ # Reusable components
│ ├── BaseButton.vue
│ ├── BaseInput.vue
│ └── BaseCard.vue
├── views/ # Page components
│ ├── Home.vue
│ ├── About.vue
│ └── Dashboard.vue
├── composables/ # Composables
│ ├── useAuth.js
│ ├── useFetch.js
│ └── useLocalStorage.js
├── stores/ # Pinia stores
│ ├── user.js
│ └── cart.js
├── utils/ # Utility functions
│ ├── helpers.js
│ └── constants.js
├── router/ # Vue Router
│ └── index.js
└── App.vue
  • Components: PascalCase (e.g., UserProfile.vue)
  • Composables: camelCase with use prefix (e.g., useAuth.js)
  • Stores: camelCase with Store suffix (e.g., userStore.js)
  • Utils: camelCase (e.g., formatDate.js)