【JavaScript_1】Vue2中Object.defineProperty响应式实现原理

  • Post author:
  • Post category:java


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()来触发响应式



版权声明:本文为ryling_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。