Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 Composition API reference—script setup macros, reactivity, lifecycle hooks, and built-in components
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/core-new-apis.md
1---2name: core-new-apis3description: Vue 3 reactivity system, lifecycle hooks, and composable patterns4---56# Reactivity, Lifecycle & Composables78## Reactivity910### ref vs shallowRef1112```ts13import { ref, shallowRef } from 'vue'1415// ref - deep reactivity (tracks nested changes)16const user = ref({ name: 'John', profile: { age: 30 } })17user.value.profile.age = 31 // Triggers reactivity1819// shallowRef - only .value assignment triggers reactivity (better performance)20const data = shallowRef({ items: [] })21data.value.items.push('new') // Does NOT trigger reactivity22data.value = { items: ['new'] } // Triggers reactivity23```2425**Prefer `shallowRef`** for large data structures or when deep reactivity is unnecessary.2627### computed2829```ts30import { ref, computed } from 'vue'3132const count = ref(0)3334// Read-only computed35const doubled = computed(() => count.value * 2)3637// Writable computed38const plusOne = computed({39get: () => count.value + 1,40set: (val) => { count.value = val - 1 }41})42```4344### reactive & readonly4546```ts47import { reactive, readonly } from 'vue'4849const state = reactive({ count: 0, nested: { value: 1 } })50state.count++ // Reactive5152const readonlyState = readonly(state)53readonlyState.count++ // Warning, mutation blocked54```5556Note: `reactive()` loses reactivity on destructuring. Use `ref()` or `toRefs()`.5758## Watchers5960### watch6162```ts63import { ref, watch } from 'vue'6465const count = ref(0)6667// Watch single ref68watch(count, (newVal, oldVal) => {69console.log(`Changed from ${oldVal} to ${newVal}`)70})7172// Watch getter73watch(74() => props.id,75(id) => fetchData(id),76{ immediate: true }77)7879// Watch multiple sources80watch([firstName, lastName], ([first, last]) => {81fullName.value = `${first} ${last}`82})8384// Deep watch with depth limit (Vue 3.5+)85watch(state, callback, { deep: 2 })8687// Once (Vue 3.4+)88watch(source, callback, { once: true })89```9091### watchEffect9293Runs immediately and auto-tracks dependencies.9495```ts96import { ref, watchEffect, onWatcherCleanup } from 'vue'9798const id = ref(1)99100watchEffect(async () => {101const controller = new AbortController()102103// Cleanup on re-run or unmount (Vue 3.5+)104onWatcherCleanup(() => controller.abort())105106const res = await fetch(`/api/${id.value}`, { signal: controller.signal })107data.value = await res.json()108})109110// Pause/resume (Vue 3.5+)111const { pause, resume, stop } = watchEffect(() => {})112pause()113resume()114stop()115```116117### Flush Timing118119```ts120// 'pre' (default) - before component update121// 'post' - after component update (access updated DOM)122// 'sync' - immediate, use with caution123124watch(source, callback, { flush: 'post' })125watchPostEffect(() => {}) // Alias for flush: 'post'126```127128## Lifecycle Hooks129130```ts131import {132onBeforeMount,133onMounted,134onBeforeUpdate,135onUpdated,136onBeforeUnmount,137onUnmounted,138onErrorCaptured,139onActivated, // KeepAlive140onDeactivated, // KeepAlive141onServerPrefetch // SSR only142} from 'vue'143144onMounted(() => {145console.log('DOM is ready')146})147148onUnmounted(() => {149// Cleanup timers, listeners, etc.150})151152// Error boundary153onErrorCaptured((err, instance, info) => {154console.error(err)155return false // Stop propagation156})157```158159## Effect Scope160161Group reactive effects for batch disposal.162163```ts164import { effectScope, onScopeDispose } from 'vue'165166const scope = effectScope()167168scope.run(() => {169const count = ref(0)170const doubled = computed(() => count.value * 2)171172watch(count, () => console.log(count.value))173174// Cleanup when scope stops175onScopeDispose(() => {176console.log('Scope disposed')177})178})179180// Dispose all effects181scope.stop()182```183184## Composables185186Composables are functions that encapsulate stateful logic using Composition API.187188### Naming Convention189190- Start with `use`: `useMouse`, `useFetch`, `useCounter`191192### Pattern193194```ts195// composables/useMouse.ts196import { ref, onMounted, onUnmounted } from 'vue'197198export function useMouse() {199const x = ref(0)200const y = ref(0)201202const update = (e: MouseEvent) => {203x.value = e.pageX204y.value = e.pageY205}206207onMounted(() => window.addEventListener('mousemove', update))208onUnmounted(() => window.removeEventListener('mousemove', update))209210return { x, y }211}212```213214### Accept Reactive Input215216Use `toValue()` (Vue 3.3+) to normalize refs, getters, or plain values.217218```ts219import { ref, watchEffect, toValue, type MaybeRefOrGetter } from 'vue'220221export function useFetch(url: MaybeRefOrGetter<string>) {222const data = ref(null)223const error = ref(null)224225watchEffect(async () => {226data.value = null227error.value = null228229try {230const res = await fetch(toValue(url))231data.value = await res.json()232} catch (e) {233error.value = e234}235})236237return { data, error }238}239240// Usage - all work:241useFetch('/api/users')242useFetch(urlRef)243useFetch(() => `/api/users/${props.id}`)244```245246### Return Refs (Not Reactive)247248Always return plain object with refs for destructuring compatibility.249250```ts251// Good - preserves reactivity when destructured252return { x, y }253254// Bad - loses reactivity when destructured255return reactive({ x, y })256```257258<!--259Source references:260- https://vuejs.org/api/reactivity-core.html261- https://vuejs.org/api/reactivity-advanced.html262- https://vuejs.org/api/composition-api-lifecycle.html263- https://vuejs.org/guide/reusability/composables.html264-->265