Appearance
响应性 API:核心 reactivity-api-core
ref() ref
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的 property .value
。
- 类型
typescript
function ref<T>(value: T): Ref<UnwrapRef<T>>
interface Ref<T> {
value: T
}
详细信息
ref 对象是可更改的,也就是说你可以为
.value
赋予新的值。它也是响应式的,即所有对.value
的操作都将被追踪,并且写操作会触发与之相关的副作用。如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。
若要避免这种深层次的转换,请使用
shallowRef()
来替代。示例
javascript
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
computed () computed
接受一个 getter 函数,返回一个只读的响应式 ref 对象,即 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
- 类型
typescript
// 只读
function computed<T>(
getter: () => T,
// 查看下方的 "计算属性调试" 链接
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
// 可写的
function computed<T>(
options: {
get: () => T
set: (value: T) => void
},
debuggerOptions?: DebuggerOptions
): Ref<T>
示例
创建一个只读的计算属性 ref:
javascript
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误
创建一个可写的计算属性 ref:
javascript
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
调试:
javascript
const plusOne = computed(() => count.value + 1, {
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
reactive() reactive
返回一个对象的响应式代理。
- 类型
typescript
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
详细信息
响应式转换是“深层”的:它会影响到所有嵌套的 property。一个响应式对象也将深层地解包任何 ref property,同时保持响应性。
值得注意的是,当访问到某个响应式数组或
Map
这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。
返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免依赖于原始对象。
示例
创建一个响应式对象:
javascript
const obj = reactive({ count: 0 })
obj.count++
ref 的解包:
typescript
const count = ref(1)
const obj = reactive({ count })
// ref 会被解包
console.log(obj.count === count.value) // true
// 会更新 `obj.count`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2
// 也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3
注意当访问到某个响应式数组或 `Map` 这样的原生集合类型中的 ref 元素时,**不会**执行 ref 的解包:
javascript
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)
将一个 ref 赋值给为一个 reactive
属性时,该 ref 会被自动解包:
typescript
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
readonly() readonly
接受一个对象 (不论是响应式还是一般的) 或是一个 ref,返回一个原值的只读代理。
- 类型
typescript
function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>>
详细信息
只读代理是深层的:对任何嵌套 property 的访问都将是只读的。它的 ref 解包行为与
reactive()
相同,但解包得到的值是只读的。要避免深层级的转换行为,请使用 shallowReadonly() 作替代。
示例
javascript
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
// 更改源属性会触发其依赖的侦听器
original.count++
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
watchEffect() watcheffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
- 类型
typescript
function watchEffect(
effect: (onCleanup: OnCleanup) => void,
options?: WatchEffectOptions
): StopHandle
type OnCleanup = (cleanupFn: () => void) => void
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // default: 'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
type StopHandle = () => void
详细信息
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
返回值是一个用来停止该副作用的函数。
示例
javascript
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
副作用清除:
javascript
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前
// 未完成的请求
onCleanup(cancel)
data.value = await response
})
停止侦听器:
javascript
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
选项:
javascript
watchEffect(() => {}, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
watchPostEffect() watchposteffect
watchEffect()
使用 flush: 'post'
选项时的别名。
watchSyncEffect() watchsynceffect
watchEffect()
使用 flush: 'sync'
选项时的别名。
watch() watch
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
- 类型
typescript
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
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 // 响应式对象
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean // 默认:false
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
为了便于阅读,对类型进行了简化。
详细信息
watch()
默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。第一个参数是侦听器的源。这个来源可以是以下几种:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- ...或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
第三个可选的参数是一个对象,支持以下这些选项:
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源是对象,强制深度遍历,以便在深层级变更时启动回调。参考深层侦听器一节。flush
:调整回调函数的刷新时机。参考回调的刷新时机一节。onTrack / onTrigger
:调试侦听器的依赖。参考调试侦听器一节。
与
watchEffect()
相比,watch()
使我们可以:- 懒执行副作用;
- 更加明确是应该由哪个状态触发侦听器重新执行;
- 可以访问所侦听状态的前一个值和当前值。
示例
侦听一个 getter 函数:
javascript
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
侦听一个 ref:
javascript
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
javascript
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
当使用 getter 函数作为源时,侦听器只在此函数的返回值变化时才会启动。如果你想让回调在深层级变更时也能启动,你需要使用 { deep: true }
强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
javascript
const state = reactive({ count: 0 })
watch(
() => state,
(newValue, oldValue) => {
// newValue === oldValue
},
{ deep: true }
)
当直接侦听一个响应式对象时,侦听器自动处于深层级模式:
javascript
const state = reactive({ count: 0 })
watch(state, () => {
/* 深层级变更状态所触发的回调 */
})
watch()
和 watchEffect()
享有相同的刷新时机和调试选项:
javascript
watch(source, callback, {
flush: 'post',
onTrack(e) {
debugger
}
})