Files
pulse-libs/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

111 lines
3.0 KiB
Markdown

# Vue 3 — Composition API Best Practices
> Extraído de skills: `vue` + `vue-composition-api-best-practices`
## `<script setup>` Padrão Ouro
```vue
<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
```ts
// 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
```vue
<!-- 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
```ts
// 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
```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