三、【Vue3】——Vue2与Vue3响应式原理区别

前言:Vue2实现响应式原理使用的是Object.defineProperty()对对象中某个属性的读取、修改进行拦截,存在新增和删除缺陷。而Vue3实现响应式原理使用的是Proxy代理,拦截对象中任意属性的变化;

一、回顾Vue2响应式原理

实现原理:

  • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

  • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

存在问题:

  • 当对象中属性过多时Object.defineProperty()需针对每个属性进行遍历实现响应式,效率不高;

  • 新增属性、删除属性, 界面不会更新;

  • 只有configurable为true时候,该属性才能从对应的对象上被删除,但源数据不会响应删除;

  • 直接通过下标修改数组, 界面不会自动更新。

    
    

新增属性代码运行演示:

三、【Vue3】——Vue2与Vue3响应式原理区别_第1张图片

删除属性代码运行演示:

三、【Vue3】——Vue2与Vue3响应式原理区别_第2张图片

二、Vue3响应式原理

实现原理:

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

  • 通过Reflect(反射): 对源对象的属性进行操作。

  • MDN文档中描述的Proxy与Reflect:

    • Proxy:developer.mozilla.org/zh-CN/docs/…
    • Reflect:developer.mozilla.org/zh-CN/docs/…
      
      

下面来详细介绍下Proxy(代理)和Reflect(反射):

Proxy:

  • 语法:let p = new Proxy(target, handler)
    • target:要使用 Proxy 包装的目标对象,此处为person源数据;
    • handler:一个对象。可以只传一个空对象,也能实现增删改查操作let p = new Proxy(person, {});可以向上述代码一样传入含有的get set deleteproperty函数来拦截对象中任意属性的变化;

get函数:

get(target, propName) {
    // target:目标对象,也就是person源数据;
    // propName:被获取的属性名;
    console.log(`target, propName`, target, propName)
    return target[propName]
},

三、【Vue3】——Vue2与Vue3响应式原理区别_第3张图片

set函数:

// set函数对新增或修改一个属性都可以拦截到
set(target, propName, value) {
   // value:新属性值
   console.log(`target, propName,value`, target, propName,value)
   target[propName] = value
},

三、【Vue3】——Vue2与Vue3响应式原理区别_第4张图片

deleteProperty函数:

deleteProperty(target, propName) {
  console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
  return delete target[propName]
}

三、【Vue3】——Vue2与Vue3响应式原理区别_第5张图片

有人注意到,我在此处展示函数阶段没有用到Reflect函数,但最开始展示原理时候却是用到的,以下解释一下Reflect作用。

Reflect:

Reflect函数身上的一些方法与Object相同,比如:

  1. Reflect.get(target,propName)相当于get函数中直接target[propName]
  2. Reflect.set(target,propName,value)相当于set函数中target[propName] = value
  3. Reflect.deleteProperty(target,propName)相当于deleteProperty函数中的delete target[propName]

当然,Reflect函数身上还有Reflect.defineProperty方法,与Object.defineProperty有所不同:

  1. Object.defineProperty对同一个对象同一个属性重复操作时,系统会报错代码运行不下去,但Reflect.defineProperty不会报错只会运行第一条结果并继续执行后边代码;

Object.defineProperty:

let obj = { a: 1, b: 2 }
        Object.defineProperty(obj, 'c', {
            get() {
                return 3
            }
        })
        Object.defineProperty(obj, 'c', {
            get() {
                return 4
            }
        })

Reflect.defineProperty:

let obj = { a: 1, b: 2 }
        const x1 = Reflect.defineProperty(obj, 'c', {
            get() {
                return 3
            }
        })
        console.log('x1',x1)

        const x2 = Reflect.defineProperty(obj, 'c', {
            get() {
                return 4
            }
        })
        console.log('@@@@@') 

  1. 以上代码演示可以看出,Reflect.defineProperty拥有返回值,且是一个布尔值,为后续逻辑判断做铺垫,不用向Object.defineProperty一样进行try...catch捕获。

注:一般组件封装用Reflect函数比较多,宽容度好,组件更健壮

你可能感兴趣的:(vue.js,javascript,前端)