Vue3.0 常用响应式API的使用和原理分析(二)
reactive对传入的类型是有限制的,必须是对象或者数组。对一些基础类型,例如string, number,boolean等不支持,如果要使用reactiveAPI必须将这些基础类型封装成对象,这样显然是不太科学的。因此Vue 3.0提供了refAPI。Ref是一个接口, 它最主要的是有一个value属性可以获取值和赋值。export interface Ref<T = any> {
reactive对传入的类型是有限制的,必须是对象或者数组。对一些基础类型,例如string, number,boolean等不支持,如果要使用reactiveAPI必须将这些基础类型封装成对象,这样显然是不太科学的。因此Vue 3.0提供了refAPI。
Ref是一个接口, 它最主要的是有一个value属性可以获取值和赋值。
export interface Ref<T = any> {
value: T
_shallow?: boolean
}
ref
使用场景
将数据变为响应式数据。

代码解释:
- 通过
ref将字符串变为了一个响应式对象person;- 通过
person.value给person进行新值的设置,也是通过person.value获取响应式对象person的值。
实现原理
createRef传入的参数如果已经是ref对象,就直接返回;如果不是就利用RefImpl进行封装。

RefImpl有两个私有变量_value和_rawValue,_rawValue是原始值,_value是操作的值。- 如果
value值是原始数据,_value和_rawValue都等于value; - 如果
value值是数组或者对象,_value被转换成了reactive响应式对象,_rawValue就是响应式对象的原始对象; get函数先收集依赖,然后返回_value是操作的值;set函数先比对原始值有没有变化,如果变化了就设置_value和_rawValue,然后分发依赖。
- 如果

ref和reactive相关的一些疑问?
问题1:基础类型数据变为响应式对象用
refAPI,对象或者数组变为响应式对象用reactiveAPI?答案1:一般是这样使用的,但是
ref也是可以将对象或者数组变为响应式对象的,因为其内部实现机制也是基于reactive。
问题2:既然
ref包含了reactive的功能,为什么不只提供refAPI就一切都搞定了。答案2:
ref的一个特点是提供了set方法, 可以将整个原值数据value完全替换掉,类似于let,而reactive是不能这样操作的,只能对原值数据value的属性进行修改, 类似于const的限制。即
ref类似于let,reactive类似于const,他们的作用场景不一样。
shallowRef
使用场景
只需要监测对象的替换,不需要监测对象的属性修改。原始数据类型
shallowRef和ref的效果没有差别。
const p1 = {name: "hehe"};
const p2 = {name: "xixi"};
// 响应式数据
const person = shallowRef(p1);
person.value.name = "haha"; // 不会监测到数据变化
person.value = p2; // 会监测到数据变化
reactive不存在替换对象的情况,所以shallowReactive是能监测到外部属性的变化,不能监测到内部属性的变化。
实现原理
shallowRef不会将对象转换成reactive对象,只有value值变化后才会分发依赖。

你可能会好奇,
person.value.name = "haha"设置新值后hasChanged(newVal, this._rawValue)不是应该true分发依赖吗?其实
person.value.name = "haha"这里调用的是get方法,调用的是get方法,调用的是get方法。和set方法没有关系哦~~~什么时候调用
set方法?当然是person.value = p2;这个方法啦。 希望没有被绕晕啊~~~
isRef
使用场景
判断一个对象是否是
ref对象
实现原理
isRef很简单,就是判断__v_isRef是否为true。因为RefImpl的__v_isRef就是true。

unref
使用场景
获取
ref对象的_value值,有可能是reactive对象(因为不是获取_rawValue的值)。
实现原理

toRef
使用场景
将
reactive响应式对象的某个属性创建一个ref对象,方便赋值和取值。
const zhangshanfeng = reactive({
name: '张三丰',
age: 100,
child: {
name: '张翠山',
age: 40,
child: {
name: '张无忌',
age: 20
}
}
})
const wuji = toRef(zhangshanfeng.child, 'child'); // 获得张无忌的ref对象
wuji.value.age += 10; // 修改张无忌的年龄
这个API的主要功能是当只需要操作响应式数据的部分数据时,将部分数据提取成为一个
ref对象,然后方便操作。例子中如果要操作张无忌的年龄得使用zhangshanfeng.child.child.age += 10,比较繁琐。这个接口也比较适合网络请求的返回值的处理,可能在某些请求中只有一部分数据是需要展示的,这部分提取出来处理就行了。
实现原理
- 用
ObjectRefImpl处理object和key;

get就是取object的key属性的值,set就是设置object的key属性的值。由于object是响应式对象,所以其实调用的就是响应式对象的get和set方法。

toRefs
使用场景
将
reactive响应式对象的每个属性创建一个ref对象,方便赋值和取值。
const zhangshanfeng = reactive({
name: '张三丰',
age: 100,
child: {
name: '张翠山',
age: 40,
child: {
name: '张无忌',
age: 20
}
}
})
const refs = toRefs(zhangshanfeng); // 获得张三丰的所以属性的refs。
// 结果
{
name: <ObjectRefImpl>{_object: zhangsanfeng, key: "name"},
age: <ObjectRefImpl>{_object: zhangsanfeng, key: "age"},
child: <ObjectRefImpl{_object: zhangsanfeng, key: "child"},
}
实现原理
- 就是对每个属性分别执行
toRef调用

customRef
自定义一个
ref对象,实现自己的功能。
下面官方给的一个防抖的例子:get方法就是返回值,set方法是延迟200毫秒才设置值,在这200毫秒如果设置了新值,就重新计时200毫秒再赋值。
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
实现原理
customRef的参数track和trigger分别是() => trackRefValue(this)和() => triggerRefValue(this),可以收集依赖和分发依赖,customRef持有返回的对象的get和set方法,这两个方法就是真正执行的赋值和取值的方法。

更多推荐


所有评论(0)