Files
pulse-memory/libs/vue/VUE3_COMPOSITION_API.md
T
Pulse ae39e45460 feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS:
- next-best-practices      v0.1.0  (CLEAN) — Next.js App Router, RSC, caching, data
- nextjs-patterns          v1.0.0  (CLEAN) — Next.js 15: Server Actions, route handlers
- vite                     v1.0.0  (CLEAN) — env vars, aliases, proxy, CJS compat
- uncle-bob                v1.0.0  (CLEAN) — Clean Code, SOLID, Clean Architecture
- clean-code-review        v1.0.0  (CLEAN) — naming, guard clauses, anti-patterns, refactoring
- vue                      v1.0.0  (CLEAN) — Vue framework
- vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity

BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos):
- typescript/ — TS safe + generics gotchas
- react/ — Next.js App Router + Vite config
- vue/ — Composition API + Pinia
- linux/ — System diagnostic cheatsheet
- database/ — PostgreSQL + MySQL patterns
- browser/ — Chromium CLI + E2E testing
- security/ — SAST audit (OWASP Top 10)
- best-practices/ — Clean Code + SOLID + Clean Architecture
- deploy/ — Docker multi-stack + OpenClaw ops
- + INDEX.md como guia de navegacao

.learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
2026-05-19 21:03:25 -03:00

3.0 KiB

Vue 3 — Composition API Best Practices

Extraído de skills: vue + vue-composition-api-best-practices

<script setup> Padrão Ouro

<script setup lang="ts">
// ✅ Importações no topo — tree-shakeable
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'

// ✅ Composables começam com `use`
const user = useUserStore()
const selectedId = ref<number | null>(null)

// ✅ Estado + getters no mesmo lugar
const filteredUsers = computed(() =>
  user.list.filter(u => u.active)
)

// ✅ Lifecycle no fim
onMounted(() => user.fetchAll())
</script>

<template>
  <button v-if="filteredUsers.length > 0" @click="selectedId = null">
    Clear
  </button>
</template>

Composables — useXxx Pattern

// composables/usePagination.ts
export function usePagination<T>(items: T[], pageSize = 20) {
  const page = ref(1)
  const totalPages = computed(() => Math.ceil(items.value.length / pageSize))
  const paginated = computed(() =>
    items.value.slice((page.value - 1) * pageSize, page.value * pageSize)
  )
  return { page, totalPages, paginated }
}

<script setup> Deep Dive

<!--  BOM  validade automática, minificado -->
<script setup lang="ts">
defineProps<{ msg: string }>()
const emit = defineEmits<{ (e: 'update', id: number): void }>()
</script>

<!--  RUIM  option API misturado -->
<script lang="ts">
export default {
  data() { return { x: 1 } },
  methods: { /* ... */ }
}
</script>

State Management — Pinia

// stores/counter.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  function increment() { count.value++ }
  return { count, double, increment }
})

Reactividade — Principais Armadilhas

  • ref vs reactive: use ref por padrão (tipagem simples); reactive para objetos grandes
  • Perda de reatividade ao desestruturar — usar toRefs() ou storeToRefs()
  • watch vs watchEffect: watch é mais controlado; watchEffect é automático mas menos previsível
  • v-if vs v-show: v-if remove do DOM; v-show togglea display

Type-Safe Vue

<script setup lang="ts">
// Props tipadas — importa de arquivo separado se for reutilizável
const props = defineProps<{
  userId: number
  items: Item[]
  requiredValue: string
}>()
const emit = defineEmits<{
  (e: 'delete', id: number): void
  (e: 'save', data: FormData): void
}>()
</script>

Vue Router Traps

  • useRoute() para rota atual — reativa, usar em setup
  • useRouter() para navegação — router.push('/path')
  • Guards: beforeEach, beforeResolve, afterEach — retornar false cancela
  • <RouterView> com named views — múltiplas views por rota

Common Mistakes

  • key em v-for é obrigatório — v-for="item in items" :key="item.id"
  • Ordem de event modifiers importa — .prevent.stop.stop.prevent
  • Teleport para modais — <Teleport to="body"> renderiza fora da árvore