Vue.js (original) (raw)

Reactivity API: Core

ref()

Takes an inner value and returns a reactive and mutable ref object, which has a single property .value that points to the inner value.

function ref<T>(value: T): Ref<UnwrapRef<T>>  
interface Ref<T> {  
  value: T  
}  
const count = ref(0)  
console.log(count.value) // 0  
count.value = 1  
console.log(count.value) // 1  

computed()

Takes a getter function and returns a readonly reactive ref object for the returned value from the getter. It can also take an object with get and set functions to create a writable ref object.

// read-only  
function computed<T>(  
  getter: (oldValue: T | undefined) => T,  
  // see "Computed Debugging" link below  
  debuggerOptions?: DebuggerOptions  
): Readonly<Ref<Readonly<T>>>  
// writable  
function computed<T>(  
  options: {  
    get: (oldValue: T | undefined) => T  
    set: (value: T) => void  
  },  
  debuggerOptions?: DebuggerOptions  
): Ref<T>  
const count = ref(1)  
const plusOne = computed(() => count.value + 1)  
console.log(plusOne.value) // 2  
plusOne.value++ // error  

Creating a writable computed ref:
js

const count = ref(1)  
const plusOne = computed({  
  get: () => count.value + 1,  
  set: (val) => {  
    count.value = val - 1  
  }  
})  
plusOne.value = 1  
console.log(count.value) // 0  

Debugging:
js

const plusOne = computed(() => count.value + 1, {  
  onTrack(e) {  
    debugger  
  },  
  onTrigger(e) {  
    debugger  
  }  
})  

reactive()

Returns a reactive proxy of the object.

function reactive<T extends object>(target: T): UnwrapNestedRefs<T>  
const obj = reactive({ count: 0 })  
obj.count++  

Ref unwrapping:
ts

const count = ref(1)  
const obj = reactive({ count })  
// ref will be unwrapped  
console.log(obj.count === count.value) // true  
// it will update `obj.count`  
count.value++  
console.log(count.value) // 2  
console.log(obj.count) // 2  
// it will also update `count` ref  
obj.count++  
console.log(obj.count) // 3  
console.log(count.value) // 3  

Note that refs are not unwrapped when accessed as array or collection elements:
js

const books = reactive([ref('Vue 3 Guide')])  
// need .value here  
console.log(books[0].value)  
const map = reactive(new Map([['count', ref(0)]]))  
// need .value here  
console.log(map.get('count').value)  

When assigning a ref to a reactive property, that ref will also be automatically unwrapped:
ts

const count = ref(1)  
const obj = reactive({})  
obj.count = count  
console.log(obj.count) // 1  
console.log(obj.count === count.value) // true  

readonly()

Takes an object (reactive or plain) or a ref and returns a readonly proxy to the original.

function readonly<T extends object>(  
  target: T  
): DeepReadonly<UnwrapNestedRefs<T>>  
const original = reactive({ count: 0 })  
const copy = readonly(original)  
watchEffect(() => {  
  // works for reactivity tracking  
  console.log(copy.count)  
})  
// mutating original will trigger watchers relying on the copy  
original.count++  
// mutating the copy will fail and result in a warning  
copy.count++ // warning!  

watchEffect()

Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.

function watchEffect(  
  effect: (onCleanup: OnCleanup) => void,  
  options?: WatchEffectOptions  
): WatchHandle  
type OnCleanup = (cleanupFn: () => void) => void  
interface WatchEffectOptions {  
  flush?: 'pre' | 'post' | 'sync' // default: 'pre'  
  onTrack?: (event: DebuggerEvent) => void  
  onTrigger?: (event: DebuggerEvent) => void  
}  
interface WatchHandle {  
  (): void // callable, same as `stop`  
  pause: () => void  
  resume: () => void  
  stop: () => void  
}  
const count = ref(0)  
watchEffect(() => console.log(count.value))  
// -> logs 0  
count.value++  
// -> logs 1  

Stopping the watcher:
js

const stop = watchEffect(() => {})  
// when the watcher is no longer needed:  
stop()  

Pausing / resuming the watcher:
js

const { stop, pause, resume } = watchEffect(() => {})  
// temporarily pause the watcher  
pause()  
// resume later  
resume()  
// stop  
stop()  

Side effect cleanup:
js

watchEffect(async (onCleanup) => {  
  const { response, cancel } = doAsyncWork(newId)  
  // `cancel` will be called if `id` changes, cancelling  
  // the previous request if it hasn't completed yet  
  onCleanup(cancel)  
  data.value = await response  
})  

Side effect cleanup in 3.5+:
js

import { onWatcherCleanup } from 'vue'  
watchEffect(async () => {  
  const { response, cancel } = doAsyncWork(newId)  
  // `cancel` will be called if `id` changes, cancelling  
  // the previous request if it hasn't completed yet  
  onWatcherCleanup(cancel)  
  data.value = await response  
})  

Options:
js

watchEffect(() => {}, {  
  flush: 'post',  
  onTrack(e) {  
    debugger  
  },  
  onTrigger(e) {  
    debugger  
  }  
})  

watchPostEffect()

Alias of watchEffect() with flush: 'post' option.

watchSyncEffect()

Alias of watchEffect() with flush: 'sync' option.

watch()

Watches one or more reactive data sources and invokes a callback function when the sources change.

// watching single source  
function watch<T>(  
  source: WatchSource<T>,  
  callback: WatchCallback<T>,  
  options?: WatchOptions  
): WatchHandle  
// watching multiple sources  
function watch<T>(  
  sources: WatchSource<T>[],  
  callback: WatchCallback<T[]>,  
  options?: WatchOptions  
): WatchHandle  
type WatchCallback<T> = (  
  value: T,  
  oldValue: T,  
  onCleanup: (cleanupFn: () => void) => void  
) => void  
type WatchSource<T> =  
  | Ref<T> // ref  
  | (() => T) // getter  
  | (T extends object ? T : never) // reactive object  
interface WatchOptions extends WatchEffectOptions {  
  immediate?: boolean // default: false  
  deep?: boolean | number // default: false  
  flush?: 'pre' | 'post' | 'sync' // default: 'pre'  
  onTrack?: (event: DebuggerEvent) => void  
  onTrigger?: (event: DebuggerEvent) => void  
  once?: boolean // default: false (3.4+)  
}  
interface WatchHandle {  
  (): void // callable, same as `stop`  
  pause: () => void  
  resume: () => void  
  stop: () => void  
}  

Types are simplified for readability.

const state = reactive({ count: 0 })  
watch(  
  () => state.count,  
  (count, prevCount) => {  
    /* ... */  
  }  
)  

Watching a ref:
js

const count = ref(0)  
watch(count, (count, prevCount) => {  
  /* ... */  
})  

When watching multiple sources, the callback receives arrays containing new / old values corresponding to the source array:
js

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {  
  /* ... */  
})  

When using a getter source, the watcher only fires if the getter's return value has changed. If you want the callback to fire even on deep mutations, you need to explicitly force the watcher into deep mode with { deep: true }. Note in deep mode, the new value and the old will be the same object if the callback was triggered by a deep mutation:
js

const state = reactive({ count: 0 })  
watch(  
  () => state,  
  (newValue, oldValue) => {  
    // newValue === oldValue  
  },  
  { deep: true }  
)  

When directly watching a reactive object, the watcher is automatically in deep mode:
js

const state = reactive({ count: 0 })  
watch(state, () => {  
  /* triggers on deep mutation to state */  
})  

watch() shares the same flush timing and debugging options with watchEffect():
js

watch(source, callback, {  
  flush: 'post',  
  onTrack(e) {  
    debugger  
  },  
  onTrigger(e) {  
    debugger  
  }  
})  

Stopping the watcher:
js

const stop = watch(source, callback)  
// when the watcher is no longer needed:  
stop()  

Pausing / resuming the watcher:
js

const { stop, pause, resume } = watch(() => {})  
// temporarily pause the watcher  
pause()  
// resume later  
resume()  
// stop  
stop()  

Side effect cleanup:
js

watch(id, async (newId, oldId, onCleanup) => {  
  const { response, cancel } = doAsyncWork(newId)  
  // `cancel` will be called if `id` changes, cancelling  
  // the previous request if it hasn't completed yet  
  onCleanup(cancel)  
  data.value = await response  
})  

Side effect cleanup in 3.5+:
js

import { onWatcherCleanup } from 'vue'  
watch(id, async (newId) => {  
  const { response, cancel } = doAsyncWork(newId)  
  onWatcherCleanup(cancel)  
  data.value = await response  
})  

onWatcherCleanup()

Register a cleanup function to be executed when the current watcher is about to re-run. Can only be called during the synchronous execution of a watchEffect effect function or watch callback function (i.e. it cannot be called after an await statement in an async function.)

function onWatcherCleanup(  
  cleanupFn: () => void,  
  failSilently?: boolean  
): void  
import { watch, onWatcherCleanup } from 'vue'  
watch(id, (newId) => {  
  const { response, cancel } = doAsyncWork(newId)  
  // `cancel` will be called if `id` changes, cancelling  
  // the previous request if it hasn't completed yet  
  onWatcherCleanup(cancel)  
})