ref()
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此ref对象只有一个指向其内部值的属性
.value
。
示例
const count = ref(0) // 初始值为0
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
// 如果将一个对象赋值给 ref,那么这个对象将通过reactive转为具有深层次响应式的对象
const refObj = ref({count: 1})
refObj.value.count++
console.log(refObj.value.count) // 2
访问$refs
// template
<form ref="formRef">...</form>
// script setup
const formRef = ref(null) // 变量名formRef 与template中form绑定的ref属性值一致,会自动识别为对应的dom
const handleSubmit = () => {
formRef.value.validate(async (valid) => {
if (valid) {
...
formRef.value.resetFields()
}
})
}
computed()
接受一个 getter 函数,返回一个只读的响应式ref对象。该 ref 通过
.value
暴露 getter 函数的返回值。它也可以接受一个带有get
和set
函数的对象来创建一个可写的 ref 对象。
示例
创建一个只读的计算属性 ref:
let firstName = ref('hello')
let lastName = ref('world')
let fullName = computed(() => firstName.value+' '+lastName.value) // 返回一个响应式ref对象
console.log(fullName.value) // hello world
firstName.value = 'goodbye'
console.log(fullName.value) // goodbye world
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误-->只读
创建一个可写的计算属性 ref:
const count = ref(1)
const plusOne = computed({
get() {
return count.value
},
set(newVal) {
count.value = newVal
}
})
plusOne.value = 2
console.log(count.value) // 2
plusOne.value = 3
console.log(count.value) // 3
reactive()
返回一个对象的响应式代理。
详细信息
- 响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
- 值得注意的是,当访问到某个响应式数组或
Map
这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。
示例
创建一个响应式对象:
const reactiveObj = reactive({count: 0})
console.log(reactiveObj.count) // 0
reactiveObj.count++
console.log(reactiveObj.count) // 1
ref解包(不需要.value):
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 的解包(需要.value):
const arr = reactive([ref('hello')])
// 这里需要 .value
console.log(arr[0].value)
const map = reactive(new Map([['name', ref('jack')],['sex', ref('man')]]))
// 这里需要 .value
console.log('map', map.get('name').value) // jack
readonly()
接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与 reactive() 相同,但解包得到的值是只读的。
示例
const original = reactive({ count: 0 })
const copy = readonly(original)
// 更改源属性会触发其依赖的侦听器
original.count++
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
详细信息
- 第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
- 第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
- 返回值是一个用来停止该副作用的函数。
示例
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1
副作用清除:
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前未完成的请求
onCleanup(cancel)
data.value = await response
})
停止侦听器:
const stop = watchEffect(() => {}) // 返回一个停止该副作用的函数
// 当不再需要此侦听器时:
stop()
选项:
watchEffect(() => {}, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
详细信息
watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
第一个参数是侦听器的源。
这个来源可以是以下几种:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- ...或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。
这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
第三个可选的参数是一个对象
支持以下这些选项:
-
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。 -
deep
:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器一节。 -
flush
:调整回调函数的刷新时机。参考回调的刷新时机一节。 -
onTrack / onTrigger
:调试侦听器的依赖。参考调试侦听器一节。
与 watchEffect()
相比,watch()
使我们可以:
- 懒执行副作用(watchEffect是立即执行);
- 更加明确是应该由哪个状态触发侦听器重新执行;
- 可以访问所侦听状态的前一个值和当前值。
示例
侦听一个 getter 函数:
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 以下是错误的,用上面的方法监听一个对象的属性
watch(state.count, (count, prevCount) => {
/* ... */
}
)
当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true } 强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
const watchGetter = reactive({count: 0})
// 不加deep: true
watch(() => watchGetter, (newVal, oldVal) => {
console.log("newVal", newVal.count)
console.log("oldVal", oldVal.count)
})
watchGetter.count = 1 // 此时不会触发回调
// 加deep: true
watch(() => watchGetter, (newVal, oldVal) => {
// 新值和旧值将是同一个对象
console.log("newVal", newVal.count) // 1
console.log("oldVal", oldVal.count) // 1
},{
deep: true
})
watchGetter.count = 1 // 此时加上deep:true会触发回调
侦听一个 ref:
const watchRef = ref(0)
watch(watchRef, (newVal) => {
console.log(newVal) // 1
})
watchRef.value = 1
侦听reactive对象:
当直接侦听一个响应式对象时,侦听器会自动启用深层模式。
const watchReactive = reactive({count: {num: 0}})
watch(watchReactive, (newVal) => {
console.log("newVal", newVal.count.num) // 1
})
watchReactive.count.num = 1
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})