前面关于响应式的两篇文章,分别介绍了响应式实现原理和计算属性,本篇文章我们来看看其他响应式API的使用和实现原理。
reactive
说明:将对象或者数组变为响应式对象主要
API之一。
使用方式
说明:
- 使用
reactive将p原始对象转为响应式对象person;- 使用
changeName可以修改响应式对象person的name的值;- 使用
changeChildName可以修改响应式对象person的child的name的值;
实现原理
请参阅本系列的Vue3.0 响应式实现原理分析的这篇文章。
shallowReactive
使用场景
修改最外层属性的时候需要执行某些操作(譬如更新DOM), 而修改内部属性时不需要执行额外的操作。
// 其他相同的代码忽略
const person = shallowReactive(p);
例子中如果修改person的name的值会触发界面的更新,但是修改person的child的name的值界面不会更新,因为修改的不是外层属性。
shallowReactive只对对象的最外层属性进行了响应式的处理,这里需要有一个响应式的概念,并不是不能修改person的child的name,只是修改了值以后不能及时在界面中显示更新后的结果。可以自己验证一下,如果先更改
person的child的name,然后再更改person的name的值会触发重新渲染,此时能看到child的name是张无忌,说明person的child的name值的修改是成功的。
实现原理
- 处理函数是浅响应式的函数
- 响应式的处理函数是
shallowGet和shallowSet
-
shallow为true,shallowGet获取属性值时直接返回原始值,不会用reactive(res)递归劫持内部属性。所以内部属性的获取是直接获取对象的属性值,不是通过Proxy的get方法获取值。
内部属性没有被
Proxy劫持,就不会收集依赖,所以内部属性的修改不会触发重新渲染更新DOM。
-
shallowSet外部属性设值是直接赋值, 内部属性也是直接赋值,因为获取到的内部属性是原对象,不是Proxy的代理对象。
readonly
使用场景
将一个对象变为只读对象,不能修改属性,也不能添加和删除属性。让对象变成一个真正意义上的只读对象。
因为对象是只读的,不需要修改,所以也不需要收集依赖和分发依赖。
提示:
const虽然不能给对象重新赋值,但是可以修改,添加和删除属性。
// 其他相同的代码忽略
const person = readOnly(p);
person.name = "张三丰"; // 报警告
person.child.name = "张无忌"; // 报警告
此时,person就是不能修改的只读对象。
实现原理
-
createReactiveObject的第二个参数是true,代表isReadonly;
- 创建响应式对象和只对对象的时候如果
target已经是响应式对象了就直接返回,但是有个特例就是对一个响应式对象执行readOnly操作是需要继续往下执行的。
-
readonlyHandlers中可以看到:设置和修改属性值的时候不做处理, 且如果是开发环境报警告。
-
get方法获取属性值时先通过Reflect获值,然后将子属性变为readOnly。
shallowReadonly
使用场景
只有最外层属性是只读的,内层属性可以进行修改。
// 其他相同的代码忽略
const person = shallowReadonly(p);
person.name = "张三丰"; // 报警告
person.child.name = "张无忌"; // 不报警告
实现原理
-
shallowReadonly重写了get方法为shallowReadonlyGet,set方法则不变。
-
shallowReadonlyGet不会将深层属性变为ReadOnly对象。和shallowReative类似,深层对象没有被劫持,是原始对象,所以可以对其属性进行修改删除等。
isReadonly
使用场景
判断某个对象是否是只读对象, 即
readOnly()函数的执行返回对象。
const readOnlyP = readonly(p);
isReadonly(readOnlyP); // true
const readOnlyPerson = readonly(person);
isReadonly(readOnlyPerson); // true
实现原理
-
isReadonly是判断__v_isReadonly对应的属性值以及属性值转换成的bool值。
- 如果
readOnly执行后的的对象获取值时候是调用get方法, 只读对象始终返回的是true:
- 如果不是只读对象,则直接获取属性值,所以如果此对象有
__v_isReadonly属性,且转换成bool值后是true,那么也会被认为是只读对象。
const a = {
name: "a",
__v_isReadonly: "0",
}
isReadonly(a); // true
const b = {
name: "b",
__v_isReadonly: "",
}
isReadonly(b); // false
虽然定义
__v_isReadonly这个属性的可能性极低,如果恰巧定义了__v_isReadonly属性,就有可能产生歧义。
isReactive
使用场景
判断某个对象是否是响应式对象, 即
reactive()函数的返回对象。
isReactive(p); // false
isReactive(person); // true
实现原理
- 判断
__v_isReactive对应的属性值以及属性值转换成的bool值, 和__v_isReadonly的逻辑类似,就不做过多介绍了。
- 如果是只读对象,通过
__v_raw判断只读对象的原始对象是否是响应式对象, 这个__v_raw的获取逻辑前面的文章也有介绍,跳过。
isProxy
使用场景
判断某个对象是否是只读对象或者响应式对象。
isProxy(person) // true
isProxy(readonly(person)) // true
实现原理
- 通过
isReactive和isReadonly进行判断
toRaw
使用场景
获取只读对象或者响应式对象的原始对象。
toRaw(person); // p对象
实现原理
- 通过
__v_raw获取原始对象,因为readonly可以作用于reactive,所以需要递归调用toRaw。
markRaw
使用场景
将对象标记为不能成为响应式对象。
const a = { content : "a" };
const raw = markRaw(a);
reactive(raw) // a 对象
注意下面的情况:
const rawPerson = markRaw(toRaw(person));
reactive(rawPerson) // person对象
疑问,不是说被
markRaw标记后的对象被标记为不能变成响应式对象吗? 为什么我们reactive(rawPerson)得到的是响应式对象?
实现原理
-
markRaw个对象添加__v_skip为true。
-
createReactiveObject如果对象的__v_skip为true,直接返回原始对象。
createReactiveObject
解答上面的问题:如果
proxyMap缓存有对应的响应式对象就直接返回了。所以即使标记了__v_skip为true也没有办法。
总结
本文我们主要了解了reactive及其相关的API的使用方式和实现原理。下篇文章我们将来分析ref相关的API。