Skip to content

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)
TipoResponsabilidadePode ter logica?Pode ter estado?
ViewsCompor componentes, fornecer contextoVia composablesSim (composables)
Componentes de FeatureUI + logica de funcionalidadeVia composablesSim (composables)
Componentes CompartilhadosUI generica e reutilizavelMinima (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-html sem sanitizacao