vue2使用Object.defineProperty监听对象属性变化,不能监听新增属性。
vue3利用Proxy实现数据绑定
vue2 利用defindeProperty实现数据绑定
defineProperty基础使用:
Object .defineproperty( obj,prop,descriptor) 限制对对象的操作
1.obj :要修改属性的对象
2.prop:要修改的属性
3.descriptor:对这个属性制定的一些规则
let obj4 = {
neme:“111”
}
Object .defineproperty( obj4 ,'age',{
value :21, //默认值
writable:true,//是否允许修改,false 不可以修改
enumerable:true,///是否允许被遍历,false 不允许被修改
configurable:true,//配置文件是否被修改
})
descriptor 详解:
函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符和存取描述符。
value和writable 是数据描述符(这几个的配置的具体含义我们下面讲)
set和get 是存取描述符
enumerable和configurable 是每次设置都可以使用的,通用描述符
前两个二选一,通用描述符每次都可以使用。
(重点)set和get这也是vue2.0前observe(观察对象属性)的实现原理
存取描述符
get 属性需要提供getter 的方法,如果没有getter 则为undefinde。该方法返回值被作为属性值,默认为undefinde
当获取该属性时,执行get
函数,属性值就是get
函数的返回值,如下
let obj = {}
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get: function () {
console.log(`你访问了obj.name属性哦`);
return 'tom';
}
})
console.log(obj.name)
set属性需要提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。
当对象的属性修改是调用set 函数,例如
let obj = {}
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
set: function (val) {
console.log(`设置obj.name的值为:${val}`)
}
})
obj.name = 'aaa'//
console.log(obj.name)
于是,我们可以利用Object.defineProperty
的特点,做各种各样的事。其中赫赫有名的就是Vue2中的响应式数据系统,就是通过Object.defineProperty
实现的!原理很简单,就是通过set
和get
监控数据变化,当数据发生变化时,可通知页面做update
等操作!
我们将对象属性监听封装
let observe = (obj) => {
// 我们有一个对象 obj,我们希望他里面所有的属性改变的时候-我们能监听到
// 我们遍历这个对象所有 属性,通过defineProperty的set 来监听
Object.keys(obj).forEach((key) => {
// 遍历了obj 的key 和 val
let val = obj[key];
// 监听属性的get和set
Object.defineProperty(obj, key, {
get() {
console.log("拦截到正在获取属性:" + key);
// 返回对应的value
return val;
},
set(newVal) {
console.log("拦截到正在修改属性:" + key);
// 设置对应的value
val = newVal;
},
});
});
};
使用
let obj = { name: "zhangsan", age: 18 };
observe(obj);
obj.name = "lisi";
console.log(obj.name);
....但是这种监听,用到数组上有一些缺点....
let list = [1,2,3,4];
observe(list);
console.log(list[0]) // 拦截到正在获取属性:0
list[0] = 2; // 拦截到正在修改属性:0
list[6] = 6; // 无法拦截...
list.push(3); // 无法拦截...
可以看到,通过索引去访问或修改已经存在的元素,是可以拦截到的。如果是不存在的元素,或者是通过push
等方法去修改数组,则无法拦截。
正因为如此,vue2在实现的时候,通过重写了数组原型上的七个方法(push、pop、shift、unshift、splice、sort、reverse)来解决(具体可以看vue/src/core/observer/array.js。
vue3利用proxy实现数据绑定
和defineProperty类似,功能几乎一样,用法不同
-
defineProperty是改变原有对象
-
proxy是新建一个代理对象
-
defineProperty只能监听某一属性不能设置去全对象监听;
-
如果需要全对象监听则需要递归修改原对象相关属性
-
我们上面只是简单的实现。为什么要递归,因为对象的属性 有可能也是个属性
-
-
且defineProperty是对象的方法 数组无法监听;proxy可以对数组进行监听
Proxy用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改。这个词的原理为代理,在这里可以表示由它来“代理”某些操作,译为“代理器”。
语法:const p = new Proxy(target, handler)
参数说明:
-
target
:要使用Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。 -
handler
:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p
的行为。
对象拦截封装成函数
let observe = (person) => {
let personProxy = new Proxy(person, {
get(target, key) {
console.log("正在获取属性", key);
//target 是 prerson
//返回person的key
return target[key];
},
set(target, key, val) {
console.log("正在设置属性", key);
//设置 persion 的key
target[key] = val;
return true;
},
});
return personProxy;
};
面试题:
vue2 和vue3底层实现原理?
两个版本的使⽤⽅法虽然在表⾯上没有太⼤的⼀个区别,但是在他的底层⽅⾯去看的话区别还是很⼤的.
vue2 的双向数据绑定是利⽤ES5 的⼀个 API ,Object.definePropert()对数据进⾏劫持 结合 发布订阅模式的⽅式来实现的。
vue3 中使⽤了 es6 的 ProxyAPI 对数据代理,通过 reactive() 函数给每⼀个对象都包⼀层 Proxy,通过 Proxy 监听属性的变化,从⽽
实现对数据的监控。
vue2和vue3 的响应式原理区别?
这⾥是引相⽐于vue2版本,使⽤proxy的优势如下
1.defineProperty只能监听某个属性,不能对全对象监听
可以省去遍历、闭包,递归等内容来提升效率(直接绑定整个对象即可)
2.可以监听数组,不⽤再去单独的对数组做特异性操作,通过Proxy可以直接拦截所有对象类型数据的操作,完美⽀持对数组的监听。