前面关于响应式的两篇文章,分别介绍了响应式实现原理和计算属性,本篇文章我们来看看其他响应式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。
网友评论