Vue3.0 常用响应式API的使用和原理分析(一)

409 阅读6分钟
Vue 3.0 系列文章

Vue 3.0组件的渲染流程

Vue 3.0组件的更新流程和diff算法详解

揭开Vue3.0 setup函数的神秘面纱

Vue 3.0 Props的初始化和更新流程的细节分析

Vue3.0 响应式实现原理分析

Vue 3.0 计算属性的实现原理分析

Vue3.0 常用响应式API的使用和原理分析(一)

Vue3.0 常用响应式API的使用和原理分析(二)

Vue 3.0 Provide和Inject实现共享数据

Vue 3.0 Teleport的使用和原理分析

Vue3侦听器和异步任务调度, 其中有个神秘角色

Vue3.0 指令

Vue3.0 内置指令的底层细节分析

Vue3.0 的事件绑定的实现逻辑是什么

Vue3.0 的双向绑定是如何实现的

Vue3.0的插槽是如何实现的?

探究Vue3.0的keep-alive和动态组件的实现逻辑

Vuex 4.x

Vue Router 4 的使用,一篇文章给你讲透彻

前面关于响应式的两篇文章,分别介绍了响应式实现原理计算属性,本篇文章我们来看看其他响应式API的使用和实现原理。

reactive

说明:将对象或者数组变为响应式对象主要API之一。

使用方式

reactive

说明:

  1. 使用reactivep原始对象转为响应式对象person
  2. 使用changeName可以修改响应式对象personname的值;
  3. 使用changeChildName可以修改响应式对象personchildname的值;

实现原理

请参阅本系列的Vue3.0 响应式实现原理分析的这篇文章。

shallowReactive

使用场景

修改最外层属性的时候需要执行某些操作(譬如更新DOM), 而修改内部属性时不需要执行额外的操作。

// 其他相同的代码忽略

const person = shallowReactive(p);

例子中如果修改personname的值会触发界面的更新,但是修改personchildname的值界面不会更新,因为修改的不是外层属性。

shallowReactive只对对象的最外层属性进行了响应式的处理,这里需要有一个响应式的概念,并不是不能修改personchildname,只是修改了值以后不能及时在界面中显示更新后的结果。

可以自己验证一下,如果先更改personchildname,然后再更改personname的值会触发重新渲染,此时能看到childname张无忌,说明personchildname值的修改是成功的。

实现原理

  • 处理函数是浅响应式的函数

在这里插入图片描述

  • 响应式的处理函数是shallowGetshallowSet

shallowGet和shallowSet

  • shallowtrueshallowGet获取属性值时直接返回原始值,不会用reactive(res)递归劫持内部属性。所以内部属性的获取是直接获取对象的属性值,不是通过Proxyget方法获取值。

内部属性没有被Proxy劫持,就不会收集依赖,所以内部属性的修改不会触发重新渲染更新DOM。

shallowGet

  • shallowSet外部属性设值是直接赋值, 内部属性也是直接赋值,因为获取到的内部属性是原对象,不是Proxy的代理对象。

shallowSet

readonly

使用场景

将一个对象变为只读对象,不能修改属性,也不能添加和删除属性。让对象变成一个真正意义上的只读对象。

因为对象是只读的,不需要修改,所以也不需要收集依赖和分发依赖。

提示:const虽然不能给对象重新赋值,但是可以修改,添加和删除属性。

// 其他相同的代码忽略

const person = readOnly(p);

person.name = "张三丰";         // 报警告
person.child.name = "张无忌";   // 报警告

此时,person就是不能修改的只读对象

实现原理

  • createReactiveObject的第二个参数是true,代表isReadonly;

readonly

  • 创建响应式对象只对对象的时候如果target已经是响应式对象了就直接返回,但是有个特例就是对一个响应式对象执行readOnly操作是需要继续往下执行的。

createReactiveObject

  • readonlyHandlers中可以看到:设置和修改属性值的时候不做处理, 且如果是开发环境报警告。

在这里插入图片描述

  • get方法获取属性值时先通过Reflect获值,然后将子属性变为readOnly

get

shallowReadonly

使用场景

只有最外层属性是只读的,内层属性可以进行修改。

// 其他相同的代码忽略

const person = shallowReadonly(p);

person.name = "张三丰";         // 报警告
person.child.name = "张无忌";   // 不报警告

实现原理

  • shallowReadonly重写了get方法为shallowReadonlyGet, set方法则不变。

shallowReadonlyHandlers

  • shallowReadonlyGet不会将深层属性变为ReadOnly对象。和shallowReative类似,深层对象没有被劫持,是原始对象,所以可以对其属性进行修改删除等。

在这里插入图片描述

isReadonly

使用场景

判断某个对象是否是只读对象, 即readOnly()函数的执行返回对象。

const readOnlyP = readonly(p);
isReadonly(readOnlyP);       // true

const readOnlyPerson = readonly(person);
isReadonly(readOnlyPerson);  // true

实现原理

  • isReadonly是判断__v_isReadonly对应的属性值以及属性值转换成的bool值。

isReadonly

  • 如果readOnly执行后的的对象获取值时候是调用get方法, 只读对象始终返回的是true

get

  • 如果不是只读对象,则直接获取属性值,所以如果此对象有__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的逻辑类似,就不做过多介绍了。

isReactive

  • 如果是只读对象,通过__v_raw判断只读对象原始对象是否是响应式对象, 这个__v_raw的获取逻辑前面的文章也有介绍,跳过。

isProxy

使用场景

判断某个对象是否是只读对象或者响应式对象

isProxy(person) // true
isProxy(readonly(person)) // true

实现原理

  • 通过isReactiveisReadonly进行判断

isProxy

toRaw

使用场景

获取只读对象或者响应式对象原始对象

toRaw(person); // p对象

实现原理

  • 通过__v_raw获取原始对象,因为readonly可以作用于reactive,所以需要递归调用toRaw

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_skiptrue

markRaw

  • createReactiveObject如果对象的__v_skiptrue,直接返回原始对象。 createReactiveObject

解答上面的问题:如果proxyMap缓存有对应的响应式对象就直接返回了。所以即使标记了__v_skiptrue也没有办法。

总结

本文我们主要了解了reactive及其相关的API的使用方式和实现原理。下篇文章我们将来分析ref相关的API