Padroes de Componentes
Nota sobre Framework
Os exemplos abaixo utilizam os padroes do pack Vue 3. Cada framework pack (React, Next.js, SvelteKit) fornece padroes equivalentes adaptados ao seu ecossistema. Veja Framework Packs para detalhes.
Template SFC Padrao
vue
<script setup lang="ts">
// 1. Imports
import { ref, computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useMarketplaceList } from '../composables/useMarketplaceList'
import { useMarketplaceStore } from '../stores/marketplace-store'
import MarketplaceCard from './MarketplaceCard.vue'
import type { MarketplaceItem } from '../types/marketplace.contracts'
// 2. Props & Emits (baseados em tipo)
interface Props {
categoryFilter?: string
}
interface Emits {
(e: 'select', item: MarketplaceItem): void
}
const props = withDefaults(defineProps<Props>(), {
categoryFilter: undefined,
})
const emit = defineEmits<Emits>()
// 3. Stores (com storeToRefs)
const store = useMarketplaceStore()
const { searchQuery, viewMode } = storeToRefs(store)
// 4. Composables
const page = ref(1)
const { items, totalPages, isLoading, isEmpty } = useMarketplaceList({
page,
search: searchQuery,
})
// 5. Estado local
const selectedId = ref<string | null>(null)
// 6. Computed
const isFirstPage = computed(() => page.value === 1)
// 7. Handlers
function handleSelect(item: MarketplaceItem) {
selectedId.value = item.id
emit('select', item)
}
</script>
<template>
<!-- ... -->
</template>
<style scoped>
/* ... */
</style>Evite Prop Drilling
Use Slots para Composicao
vue
<!-- MarketplaceView.vue -->
<template>
<PageLayout>
<template #header>
<MarketplaceFilters />
</template>
<template #content>
<MarketplaceList @select="handleSelect">
<template #card="{ item }">
<MarketplaceCard :item="item" />
</template>
<template #empty>
<EmptyState message="No items found" />
</template>
</MarketplaceList>
</template>
<template #sidebar>
<MarketplaceDetails v-if="selectedItem" :item="selectedItem" />
</template>
</PageLayout>
</template>Use Provide/Inject para Contexto Compartilhado
typescript
// composables/useMarketplaceContext.ts
import type { InjectionKey, Ref } from 'vue'
interface MarketplaceContext {
selectedItem: Ref<MarketplaceItem | null>
selectItem: (item: MarketplaceItem) => void
clearSelection: () => void
}
export const MARKETPLACE_CONTEXT: InjectionKey<MarketplaceContext> =
Symbol('marketplace-context')
export function provideMarketplaceContext() {
const selectedItem = ref<MarketplaceItem | null>(null)
function selectItem(item: MarketplaceItem) {
selectedItem.value = item
}
function clearSelection() {
selectedItem.value = null
}
const context: MarketplaceContext = {
selectedItem: readonly(selectedItem),
selectItem,
clearSelection,
}
provide(MARKETPLACE_CONTEXT, context)
return context
}
export function useMarketplaceContext() {
const context = inject(MARKETPLACE_CONTEXT)
if (!context) {
throw new Error('useMarketplaceContext must be used within a MarketplaceView')
}
return context
}Hierarquia de Componentes
Views (Paginas) → Composicao, orquestracao, fornecer contexto
└── Layout → Estrutura visual (slots)
└── Features → Logica de funcionalidade (composables, stores)
└── Shared → Apresentacao pura (props entrada, eventos saida)| Tipo | Responsabilidade | Pode ter logica? | Pode ter estado? |
|---|---|---|---|
| Views | Compor componentes, fornecer contexto | Via composables | Sim (composables) |
| Componentes de Feature | UI + logica de funcionalidade | Via composables | Sim (composables) |
| Componentes Compartilhados | UI generica e reutilizavel | Minima (apenas UI) | Minimo (local) |
Limites de Tamanho
- Total do SFC: < 200 linhas
- Template: < 100 linhas
- Se maior → decomponha em sub-componentes
Checklist
- [ ]
<script setup lang="ts"> - [ ] Props baseados em tipo (
defineProps<T>()) - [ ] Emits baseados em tipo (
defineEmits<T>()) - [ ] Sem prop drilling (use composicao / provide-inject)
- [ ] Estados de carregamento / erro / vazio
- [ ] Sem logica de negocio no template
- [ ] Sem
v-htmlsem sanitizacao