4、compute计算属性实现原理

213 阅读2分钟

一、compute计算属性的特点:

1、计算属性:解决模板中复杂的逻辑运算的问题;

2、计算属性只在内部逻辑依赖的数据发生变化的时候才会被再次调用;

3、计算属性缓存机制:当依赖发生改变时执行的方法,将结果挂载到实例上,以方法的名字为属性名,下次访问改属性时直接从实例上取值;

4、多次复用相同值的数据,计算属性只调用一次;

5、关注点在模板,处理模板中复杂夫人逻辑运算,将结果缓存起来给,模板使用。

二、实现compute的依赖收集:

1、把data返回的对象做响应式处理,遍历data每个属性通过Object.defineProperty给实例定义响应式属性;

2、收集compute对象中相关的依赖保存在computeData的对象中:

var computeData = {
[方法名]:{
value:方法执行的结果,
get:方法,
deep:[方法内部的依赖属性]
};

3、遍历computeData,通过Object.defineProperty给实例添加[方法名] 属性,之所以能够通过[方法名]直接访问到值,就是这个操作的原因。

4、当数据改变时,这个改变的属性的key给updata的方法,

5、updata方法通过key断computeData中有没有方法依赖到这个key,如果有就执行对于的get方法,将计算好的结果保存到实例[方法名] 属性中。

var App = {
	data() {
		return {
			a: 1,
			b: 2
		}
	},
	compute: {
		total() {
			console.log('有依赖改变了...');
			return this.a + this.b;
		}
	}
}

var myVue = (function() {
	var computeData = {};

	function Vue3(options) {
		this._$data = options.data();
		init(this, options.compute)
	}

	function init(vm, compute) {
		reactiveData(vm)
		reactiveCompute(vm, compute)
	}

	function reactiveData(vm) {
		var _data = vm._$data;
		for (let key in _data) {
			(function(k) {
				Reflect.defineProperty(vm, k, {
					get() {
						return vm._$data[k]
					},
					set(newValue) {
						vm._$data[k] = newValue;
						update(k)
					}
				})
			}(key))
		}
	}

	function reactiveCompute(vm, compute) {
		_initcomputeData(vm, compute)
		for (let key in computeData) {
			(function(k) {
				Reflect.defineProperty(vm, k, {
					get() {
						return computeData[k].value
					},
					set(newValue) {
						computeData[k].value = newValue
					}
				})
			}(key))
		}
	}

	function _initcomputeData(vm, compute) {
		for (let key in compute) {
			var descriptor = Object.getOwnPropertyDescriptor(compute, key),
				fn = descriptor.value.get ? descriptor.value.get : descriptor.value
			computeData[key] = {}
			computeData[key].value = fn.call(vm)
			computeData[key].get = fn.bind(vm)
			computeData[key].deep = _collectionDeep(fn)
		}
	}

	function _collectionDeep(fn) {
		var _collection = fn.toString().match(/this.(.+?)/g);
		for (var i = 0; i < _collection.length; i++) {
			_collection[i] = _collection[i].split('.')[1]
		}
		return _collection;
	}

	function update(key) {
		
	var data=Object.values(computeData)
		for (let item of data) {
			if(item.deep.includes(key)){
				item.value=item.get()
			}
		}
	}

	return Vue3;
}())

var vm = new myVue(App)
console.log(vm.total);
console.log(vm.total);
vm.a = 10 ;
console.log(vm.total);
console.log(vm.total);

image.png