feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
264
skills/vue/references/core-new-apis.md
Normal file
264
skills/vue/references/core-new-apis.md
Normal file
@@ -0,0 +1,264 @@
|
||||
---
|
||||
name: core-new-apis
|
||||
description: Vue 3 reactivity system, lifecycle hooks, and composable patterns
|
||||
---
|
||||
|
||||
# Reactivity, Lifecycle & Composables
|
||||
|
||||
## Reactivity
|
||||
|
||||
### ref vs shallowRef
|
||||
|
||||
```ts
|
||||
import { ref, shallowRef } from 'vue'
|
||||
|
||||
// ref - deep reactivity (tracks nested changes)
|
||||
const user = ref({ name: 'John', profile: { age: 30 } })
|
||||
user.value.profile.age = 31 // Triggers reactivity
|
||||
|
||||
// shallowRef - only .value assignment triggers reactivity (better performance)
|
||||
const data = shallowRef({ items: [] })
|
||||
data.value.items.push('new') // Does NOT trigger reactivity
|
||||
data.value = { items: ['new'] } // Triggers reactivity
|
||||
```
|
||||
|
||||
**Prefer `shallowRef`** for large data structures or when deep reactivity is unnecessary.
|
||||
|
||||
### computed
|
||||
|
||||
```ts
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
// Read-only computed
|
||||
const doubled = computed(() => count.value * 2)
|
||||
|
||||
// Writable computed
|
||||
const plusOne = computed({
|
||||
get: () => count.value + 1,
|
||||
set: (val) => { count.value = val - 1 }
|
||||
})
|
||||
```
|
||||
|
||||
### reactive & readonly
|
||||
|
||||
```ts
|
||||
import { reactive, readonly } from 'vue'
|
||||
|
||||
const state = reactive({ count: 0, nested: { value: 1 } })
|
||||
state.count++ // Reactive
|
||||
|
||||
const readonlyState = readonly(state)
|
||||
readonlyState.count++ // Warning, mutation blocked
|
||||
```
|
||||
|
||||
Note: `reactive()` loses reactivity on destructuring. Use `ref()` or `toRefs()`.
|
||||
|
||||
## Watchers
|
||||
|
||||
### watch
|
||||
|
||||
```ts
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
// Watch single ref
|
||||
watch(count, (newVal, oldVal) => {
|
||||
console.log(`Changed from ${oldVal} to ${newVal}`)
|
||||
})
|
||||
|
||||
// Watch getter
|
||||
watch(
|
||||
() => props.id,
|
||||
(id) => fetchData(id),
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// Watch multiple sources
|
||||
watch([firstName, lastName], ([first, last]) => {
|
||||
fullName.value = `${first} ${last}`
|
||||
})
|
||||
|
||||
// Deep watch with depth limit (Vue 3.5+)
|
||||
watch(state, callback, { deep: 2 })
|
||||
|
||||
// Once (Vue 3.4+)
|
||||
watch(source, callback, { once: true })
|
||||
```
|
||||
|
||||
### watchEffect
|
||||
|
||||
Runs immediately and auto-tracks dependencies.
|
||||
|
||||
```ts
|
||||
import { ref, watchEffect, onWatcherCleanup } from 'vue'
|
||||
|
||||
const id = ref(1)
|
||||
|
||||
watchEffect(async () => {
|
||||
const controller = new AbortController()
|
||||
|
||||
// Cleanup on re-run or unmount (Vue 3.5+)
|
||||
onWatcherCleanup(() => controller.abort())
|
||||
|
||||
const res = await fetch(`/api/${id.value}`, { signal: controller.signal })
|
||||
data.value = await res.json()
|
||||
})
|
||||
|
||||
// Pause/resume (Vue 3.5+)
|
||||
const { pause, resume, stop } = watchEffect(() => {})
|
||||
pause()
|
||||
resume()
|
||||
stop()
|
||||
```
|
||||
|
||||
### Flush Timing
|
||||
|
||||
```ts
|
||||
// 'pre' (default) - before component update
|
||||
// 'post' - after component update (access updated DOM)
|
||||
// 'sync' - immediate, use with caution
|
||||
|
||||
watch(source, callback, { flush: 'post' })
|
||||
watchPostEffect(() => {}) // Alias for flush: 'post'
|
||||
```
|
||||
|
||||
## Lifecycle Hooks
|
||||
|
||||
```ts
|
||||
import {
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
onBeforeUpdate,
|
||||
onUpdated,
|
||||
onBeforeUnmount,
|
||||
onUnmounted,
|
||||
onErrorCaptured,
|
||||
onActivated, // KeepAlive
|
||||
onDeactivated, // KeepAlive
|
||||
onServerPrefetch // SSR only
|
||||
} from 'vue'
|
||||
|
||||
onMounted(() => {
|
||||
console.log('DOM is ready')
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// Cleanup timers, listeners, etc.
|
||||
})
|
||||
|
||||
// Error boundary
|
||||
onErrorCaptured((err, instance, info) => {
|
||||
console.error(err)
|
||||
return false // Stop propagation
|
||||
})
|
||||
```
|
||||
|
||||
## Effect Scope
|
||||
|
||||
Group reactive effects for batch disposal.
|
||||
|
||||
```ts
|
||||
import { effectScope, onScopeDispose } from 'vue'
|
||||
|
||||
const scope = effectScope()
|
||||
|
||||
scope.run(() => {
|
||||
const count = ref(0)
|
||||
const doubled = computed(() => count.value * 2)
|
||||
|
||||
watch(count, () => console.log(count.value))
|
||||
|
||||
// Cleanup when scope stops
|
||||
onScopeDispose(() => {
|
||||
console.log('Scope disposed')
|
||||
})
|
||||
})
|
||||
|
||||
// Dispose all effects
|
||||
scope.stop()
|
||||
```
|
||||
|
||||
## Composables
|
||||
|
||||
Composables are functions that encapsulate stateful logic using Composition API.
|
||||
|
||||
### Naming Convention
|
||||
|
||||
- Start with `use`: `useMouse`, `useFetch`, `useCounter`
|
||||
|
||||
### Pattern
|
||||
|
||||
```ts
|
||||
// composables/useMouse.ts
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
export function useMouse() {
|
||||
const x = ref(0)
|
||||
const y = ref(0)
|
||||
|
||||
const update = (e: MouseEvent) => {
|
||||
x.value = e.pageX
|
||||
y.value = e.pageY
|
||||
}
|
||||
|
||||
onMounted(() => window.addEventListener('mousemove', update))
|
||||
onUnmounted(() => window.removeEventListener('mousemove', update))
|
||||
|
||||
return { x, y }
|
||||
}
|
||||
```
|
||||
|
||||
### Accept Reactive Input
|
||||
|
||||
Use `toValue()` (Vue 3.3+) to normalize refs, getters, or plain values.
|
||||
|
||||
```ts
|
||||
import { ref, watchEffect, toValue, type MaybeRefOrGetter } from 'vue'
|
||||
|
||||
export function useFetch(url: MaybeRefOrGetter<string>) {
|
||||
const data = ref(null)
|
||||
const error = ref(null)
|
||||
|
||||
watchEffect(async () => {
|
||||
data.value = null
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const res = await fetch(toValue(url))
|
||||
data.value = await res.json()
|
||||
} catch (e) {
|
||||
error.value = e
|
||||
}
|
||||
})
|
||||
|
||||
return { data, error }
|
||||
}
|
||||
|
||||
// Usage - all work:
|
||||
useFetch('/api/users')
|
||||
useFetch(urlRef)
|
||||
useFetch(() => `/api/users/${props.id}`)
|
||||
```
|
||||
|
||||
### Return Refs (Not Reactive)
|
||||
|
||||
Always return plain object with refs for destructuring compatibility.
|
||||
|
||||
```ts
|
||||
// Good - preserves reactivity when destructured
|
||||
return { x, y }
|
||||
|
||||
// Bad - loses reactivity when destructured
|
||||
return reactive({ x, y })
|
||||
```
|
||||
|
||||
<!--
|
||||
Source references:
|
||||
- https://vuejs.org/api/reactivity-core.html
|
||||
- https://vuejs.org/api/reactivity-advanced.html
|
||||
- https://vuejs.org/api/composition-api-lifecycle.html
|
||||
- https://vuejs.org/guide/reusability/composables.html
|
||||
-->
|
||||
Reference in New Issue
Block a user