Vue中的响应式是指,data()中的数据一旦发生了改变,页面就会自动进行相应的改变,给予用户一个网页在随着用户操作进行“响应式”更新的感受。
所以需要解决的就是,如何监听到数据的改变呢?因为这样在能在数据改变的一瞬间去做出一些更新页面的操作呀,答案就是用Object.defineProperty()来解决。
Object.defineProperty()是JavaScript中提供的一个接口,用来给一个对象新增一个属性并定义属性描述符,或是对对象的一个已有属性更改值、定义属性描述符,可以接收“对象,属性名,属性描述符”三个参数。
Object.defineProperty()的官网讲解
1.如何实现简单的响应式
以下面代码为例:
【下面代码的目的在于,如何让data这个
对象中属性的修改被监听到
,即如何
把data变为响应式的
】
const data = {};
let name = 'Origin Name';
Object.defineProperty(data, 'name', {
get: function() {
console.log('get: 已监听到data的name属性被访问');
// 返回name值
return name;
},
set: function(newName) {
// 修改name值
name = newName;
console.log('set: 已监听到data的name属性被修改,现在需要执行相应的页面更新操作了');
}
});
console.log(data.name);
data.name = 'Modify Name';
console.log(data.name);
/* 执行上述代码,依次输出:
get: 已监听到data的name属性被访问
Origin Name
set: 已监听到data的name属性被修改,现在需要执行相应的页面更新操作了
get: 已监听到data的name属性被访问
Modify Name
*/
2.如何实现深度监听
深度监听就是指,如果上面那个例子中的data对象中,包含了一个对象类型的属性,如何
监听到data属性的属性
。
首先先将上面的Object.defineProperty()封装成一个函数,方便后续调用:
// 将data变成响应式数据
function observer(data) {
// 只监听数组和对象
if (typeof data !== 'object' || data === null) {
return data;
}
// 对data中的每一项都进行监听
for (let key in data) {
myDefineProperty(data, key, data[key]);
}
}
// 简单将Object.defineProperty()封装成函数的形式
function myDefineProperty(data, key, value) {
// observer(value); // 这行代码现在还不用
Object.defineProperty(data, key, {
get: function () {
console.log('get');
return value;
},
set: function (newValue) {
if (newValue !== value) {
value = newValue;
console.log('数据已更新完毕,需要执行相应的页面更新操作了');
}
}
});
}
然后更改hobby中的数据,查看输出:
const data = {
name: 'Origin Name',
hobby: ['movie', 'cat', 'walk'],
};
// 将data变成响应式数据
observer(data);
const data = {
name: 'Origin Name',
objs: {
first: 'one',
second: 'two'
}
};
// 给data添加数据监听
observer(data);
console.log(data.objs);
data.objs.first = 'first';
console.log(data.objs);
// 执行上述代码,会输出:get {first: 'one', second: 'two'}
发现objs的中属性值的变化,并没有被监听到。
解决方法就是把myDefineProperty()中被注释掉的那行代码放出来,这样,如果判断到value也是对象或数组的数据类型的话,就把这个value也用Object.defineProperty()监听起来。
【在这里留个坑:如果data中有一个属性本身是一个数组的时候,设置其数组值的监听需要用到数组原型,这次先不写了】
形参data – 实参data
形参key – 实参data中的属性名
形参value – 实参data中的属性值
另外,在set()中其实也要observe(newValue),新设置的属性也要监听一下。
3.这种数据监听方法带来的问题
(1)如果data的层数很深,耗时就会比较久。
在vue3中使用proxy代替了Object.defineProperty()来解决这一问题。
(2)如果data删除一个属性,或新增一个属性,JavaScript原生的Object.defineProperty()也没办法监听到,所以要用Vue.delete()和Vue.set()来触发响应式