# Proxy和defineProperty实现数据观察(观察者模式)

vue双向绑定原理,是通过defineProperty实现对数据的的观察,vue3.0改成了Proxy。

# defineProperty

vue中,有$set方法来强制视图更新,当然也有数组的一些操作。其实这是defineProperty 来观察对象或者数组的一些缺陷。简单实现一个defineProperty观察数据的方法。

const person = {
  name: 'haorooms',
  age: 20
};
Object.keys(person).forEach(function(key) {
  Object.defineProperty(person, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get');
    },
    set: function(newVal) {
      // 当属性值发生变化时我们可以进行额外操作
      console.log(`欢迎大家来到${newVal}`);
    },
  });
});
person.name = 'haorooms前端博客';
//欢迎大家来到haorooms前端博客
//"haorooms前端博客"

这样就实现了对people的观察

# Proxy

用代理模式Proxy实现观察者

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}
const person = observable({
  name: 'haorooms',
  age: 5
});

function print() {
  console.log(`${person.name}, ${person.age}年了`)
}
observe(print);
person.name = 'haorooms前端博客已经有';

打印出

haorooms前端博客已经有, 5年了
"haorooms前端博客已经有"

另外,其实defineProperty能监听到数组下标的变化,只是vue性能会差!所以才做了变通???

function defineReactive(data, key, value) {
 Object.defineProperty(data, key, {
	 
 // enumerable: true,
 // configurable: true,
 get: function () {

 console.log(`get key: ${key} value: ${value}`)
 return value
 },
 set: function (newVal) {
 console.log(`set key: ${key} value: ${newVal}`)
 value = newVal
 }
 })
}
 
function observe(data) {
  console.log(data)
 Object.keys(data).forEach(function(key) {
 defineReactive(data, key, data[key])
 })
}
 
let arr = [1, 2, 3]
observe(arr)
arr[4]=1000
console.log(arr)

并且,Object.defineProperty深度监听需要递归到底,计算量大,无法监听新增属性和删除属性(使用Vue.set Vue.delete){不过也许在vue3中他们就可以退出舞台了}

//vue变异监听数组的功能基本实现
		function updateView(){
				console.log("视图更新")
		}		
		const oldArrayProperty = Array.prototype;
		const arrProto = Object.create(oldArrayProperty);
		['push', 'pop', 'shift', 'unshift', 'slice', 'splice'].forEach((methodName) => {
			arrProto[methodName] = function(){
				updateView();
				oldArrayProperty[methodName].call(this, ...arguments)
			}
		})

		function defineReactive (target, key, value) {
			observer(value)
			Object.defineProperty(target, key, {
				get () {
					return value;
				},
				set (newValue) {
					if (newValue != value) {
						observer(newValue)
						value = newValue
						updateView()
					}
				}
			})
		}

		function observer(target) {
			if (typeof target !== 'object' || target == null) {
				return target
			}

			if (Array.isArray(target)) {
				target.__proto__ = arrProto;
			}

			for (let key in target) {
				defineReactive(target, key, target[key])
			}
		}

		const data = {
			name: 'zhangsan',
			age: 20,
			info: {
				address: 'beijing'
			},
			nums: [1,2,3]
		}

		observer(data)
		
		data.age=40;
		data.nums.push(1000)

原文地址 (opens new window)

最后更新: 3/19/2021, 4:10:54 PM