Vue2.0的响应式原理+异步更新队列的实现

  • Vue 2.0 Object.defineProperty
  • Vue 2.0中 使用Object.defineProperty来将Data中的属性都遍历一遍,转换为getter和setter

 

  • getter为依赖收集 当变量修改时,可能涉及到很多地方依赖变量,所以将模板中依赖这个变量的地方都收集起来
  • setter为通知依赖更新

 

  • Vue 3.0 Proxy
  • 就是对getter和setter进行一次代理,去做到依赖收集和更新操作

 

这里我们先说一下vue2.0的响应式原理

首先有这么一个例子:

let x;
let y;
let f = n => n*100+100;

let onChange = function(){};

x = 1;

onChange(() => {
	y = f(x);
	console.log(y);
});

x = 2;
x = 3;

思考:我们如何使当x值发生变化时,自动打印出值呢

要点:Object.defineProperty

我们先上代码:

一:基本实现

let x;
let y;
let f = n => n*100+100;

//定义变量存储方法
let active;
let onChange = function(cb){
    //赋值给active
	active = cb;
    //执行
	active();
};

//定义ref,返回一个Object,defineProperty
let ref = initValue => {
	let value = initValue;
	return Object.defineProperty({},'value',{
        //直接返回
		get(){
			return value;
		},
        //重新赋值
		set(newValue){
		       value = newValue;
            //修改时执行active方法
                       active();
		}
	});
}

//首先把x改为应用类型
x = ref(1);

onChange(() => {
	y = f(x.value);
	console.log(y);
});

x.value = 2;
x.value = 3;

二:依赖添加执行操作

那么,现在如果有很多个方法,一个active肯定是不够用的,那么我们接着改造代码

let x;
let y;
let f = n => n*100+100;

//定义变量存储方法
let active;
let onChange = function(cb){
    //赋值给active
	active = cb;
    //执行
	active();
};

// 定义一个类去收集执行依赖
class Dep{
	deps = new Set();
	// 添加依赖收集
	depend(){
		if(active){
			this.deps.add(active);
		}
	}
	// 通知依赖执行
	notify(){
		this.deps.forEach(dep => dep());
	}
}

//定义ref,返回一个Object,defineProperty
let ref = initValue => {
	let value = initValue;
	let dep = new Dep();
	return Object.defineProperty({},'value',{
        //直接返回
		get(){
			// get时就添加依赖
			dep.depend();
			return value;
		},
        //重新赋值
		set(newValue){
			value = newValue;
            //修改时执行active方法
			// active();
			// 这里就不需要active了,直接通知依赖执行
			dep.notify();
		}
	});
}

//首先把x改为应用类型
x = ref(1);

onChange(() => {
	y = f(x.value);
	console.log(y);
});

x.value = 2;
x.value = 3;
x.value = 4;

//依次打印出200 300 400 500

三:添加异步更新队列操作

上面代码依次打印出了200 300 400 500,那假设我们现在有多个变量呢?多个变量数值修改,我们的更新就触发了几次,就很难受。那不妨我们添加异步更新队列,把这些数值更新全部放进微任务中,这样执行就只需要一次而不需要多次。

那就接着改造代码:

let x;
let y;
let f = n => n*100+100;

//定义变量存储方法
let active;
let watch = function(cb){
    //赋值给active
	active = cb;
    //执行
	active();
};

// 队列储存数组
let queue = [];

//异步更新队列操作,把执行的都扔进微任务中
let nextTick = cb => Promise.resolve().then(cb);

// 队列添加操作
let queueJobs = job => {
	// 如果当前队列不存在job就添加
	if(!queue.includes(job)){
		queue.push(job);
		//执行nextTick
		nextTick(flushJobs);
	}
}
// 队列执行操作
let flushJobs = () =>{
	let job;
	// 循环拿出队列第一个,不为空则执行
	while ((job = queue.shift()) !== undefined){
		job();
	}
}

// 定义一个类去收集执行依赖
class Dep{
	deps = new Set();
	// 添加依赖收集
	depend(){
		if(active){
			this.deps.add(active);
		}
	}
	// 通知依赖执行
	notify(){
		// this.deps.forEach(dep => dep());
		// 那这里的话就直接调用队列添加操作
		this.deps.forEach(dep => queueJobs(dep));
	}
}

//定义ref,返回一个Object,defineProperty
let ref = initValue => {
	let value = initValue;
	let dep = new Dep();
	return Object.defineProperty({},'value',{
        //直接返回
		get(){
			// get时就添加依赖
			dep.depend();
			return value;
		},
        //重新赋值
		set(newValue){
			value = newValue;
            //修改时执行active方法
			// active();
			// 这里就不需要active了,直接通知依赖执行
			dep.notify();
		}
	});
}

//首先把x改为应用类型
x = ref(1);

watch(() => {
	y = f(x.value);
	console.log(y);
});

x.value = 2;
x.value = 3;
x.value = 4;

// 打印出 200 500

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章