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

 

 

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