- 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