Vue3中的响应式原理 | 青训营笔记

103 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的的第15天

1. vue2.x的响应式

实现原理:

  • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
  • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
    get () {
        return data.count;
    }, 
    set (value) {
        data.count = value;
    }
})

存在问题

  • 新增属性、删除属性, 界面不会更新。
  • 直接通过下标修改数组, 界面不会自动更新。需通过数组的api才能实现,如splice,push

2. vue3.0的响应式

reactive数据响应实现原理:

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写getset、属性的添加set、属性的删除deleteProperty等。
  • 通过Reflect(反射): 对源对象的属性进行操作。

实现对person对象数据的代理响应:

let person = {
     name: "yanghi",
     age: 18,
     sex: "男"
}

// target 当前对象 prop 当前属性 value 新值
let proxyPerson = new Proxy(person, {
    // 拦截读取属性值
    get (target, prop) {
    	return Reflect.get(target, prop)
    },
    // 拦截设置属性值或添加新属性
    set (target, prop, value) {
    	return Reflect.set(target, prop, value)
    },
    // 拦截删除属性
    deleteProperty (target, prop) {
    	return Reflect.deleteProperty(target, prop)
    }
})

提出问题: 为什么要使用**Reflect(反射)API** 对原属性进行修改,而不使用 Object.defineProperty() API 对原属性进行修改呢 ?

  • 为了保持程序的健壮性,因为 Reflect(反射)API 对原属性进行修改返回一个布尔值,可用于判断是否修改成功,即使未修改成功也不会报错。
  • Object.defineProperty() API 对原属性进行修改,如果未成功,会产生错误,那么我们需要进行 try catch包裹,这样的话我们的代码很糟糕。

如下使用 Object.defineProperty() API 对原属性进行修改:

let person = {
    name: "yanghi",
    age: 18
}

try{
   	Object.defineProperty(person, 'name', {
   	 get () {
        	return person.name;
   	 	}, 
   	 set (value) {
        	person.name = value;
    	}
	});
    
    Object.defineProperty(person, 'age', {
    	get () {
        	return person.age;
    	}, 
    	set (value) {
        	person.age = value;
    	}
	});
    
}catch(err){
   console.log(err);
}

使用(proxy)代理的好处:

  • 可直接新增属性和删除属性,界面会自动更新
  • 可直接通过下标修改数组,界面会自动更新

3. reactive 对比 ref

  • 从定义数据角度对比:

    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:

    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

参考文档