- 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