接本人之前的一篇关于Vue2.0文章,可移步先浏览一遍,再阅读本章,效果更佳,理解更清晰。
点击下方立即查看~
众所周知,Vue 3.0 使用的是Proxy,是对getter和setter进行一次代理,去做到依赖收集和更新操作。而为什么要改为Proxy,是因为2.0中的defineProperty不能监听到新增的属性,还提供了一个set方法解决该问题。
那么接着改造我们之前的代码,改成Proxy
let x;
let y;
let f = n => n*100+100;
//定义变量存储方法
let active;
//定义effect函数为计算属性缓存用
//接受需要执行的函数以及扩展值
let effect = (fn,options = {}) => {
let effect = (...args) => {
try {
//赋值给active
active = effect;
//执行
return fn(...args);
} finally{
active = null;
}
}
//在effect上添加我们的options
effect.options = options;
//定义一个数组方便依赖查找
effect.deps = [];
return effect;
}
//依赖清除
let cleanUpEffect = (effect) => {
const { deps } = effect;
if(deps.length){
for(let i=0;i<deps.length;i++){
deps[i].delete(effect);
}
}
}
let watchEffect = function(cb){
let runner = effect(cb);
runner();
//3.0stop函数
return () => {
cleanUpEffect(runner);
}
};
// 队列储存数组
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);
//反向依赖添加,双向索引
active.deps.push(this.deps);
}
}
// 通知依赖执行
notify(){
// this.deps.forEach(dep => dep());
// 那这里的话就直接调用队列添加操作
this.deps.forEach(dep => queueJobs(dep));
//notify是数据变化后,所以在此,每一个依赖上如果有存在schedular的话便可执行,更改dirty为true,使得computed中去重新计算
this.deps.forEach(dep => {
dep.options && dep.options.schedular && dep.options.schedular()
})
}
}
//因为set方法实现也需要Object.defineProperty,那避免代码重复抽样一个方法
let createReactive = (target, prop, value) => {
target._dep = new Dep();
return new Proxy(target,{
//target 代理的对象
//prop 读取的属性名称
//value 设置属性的值
get(target,prop){
//添加依赖
target._dep.depend();
return Reflect.get(target,prop);
},
set(target,prop,value){
//通知依赖
target._dep.notify();
return Reflect.set(target,prop,value);
}
})
// return Object.defineProperty(target,prop,{
// //直接返回
// get(){
// // get时就添加依赖
// target._dep.depend();
// return value;
// },
// //重新赋值
// set(newValue){
// value = newValue;
// //修改时执行active方法
// // active();
// // 这里就不需要active了,直接通知依赖执行
// target._dep.notify();
// }
// });
}
//定义ref
let ref = initValue => createReactive({},"value",initValue);
//Set方法
//const set = (target,prop,initValue) => createReactive(target,prop,initValue);
//计算属性computed
let computed = (fn) => {
let value;
//dirty缓存的flag
let dirty = true;
let runner = effect(fn,{
//定义一个任务函数,在依赖响应式更新后进行的操作
schedular:() => {
if(!dirty){
dirty = true
}
}
})
//返回所需要的对象
return {
//获取值
get value(){
if(dirty){
//计算属性传入的fn执行赋值并返回
value = runner();
//避免重复计算
dirty = false;
}
//返回可监听的value值
return value;
}
}
}
//wacthAPI
//同样,watch也是一个函数,他接受两个参数
//1:接受一个值一个对象或一个数组,可以对一个或者多个值进行监听,也可以监听一个函数
//2:接受一个函数,即监听源改变后执行的回调函数
//3:接受可选项参数deep(深度遍历ps:这里暂不作演示),immediate(初始便执行一次)
let watch = (source, cb, options = {}) => {
const { immediate } = options;
//getter函数获取返回我们的数据源
const getter = () => {
return source();
}
let oldValue;
//执行函数
const runner = effect(getter,{
//定义一个任务函数,在依赖响应式更新后进行watch的回调函数
schedular:() => applyCb(),
})
const applyCb = () => {
let newValue = runner();
if(newValue != oldValue){
cb(newValue,oldValue);
oldValue = newValue;
}
}
if(immediate){
applyCb();
}else{
oldValue = runner();
}
}
//数组操作
// push
// pop
// shift
// unshift
// splice
// sort
// reverse
//2.0中会对数组方法进行一次代理
//举例push
// let push = Array.prototype.push;
// Array.prototype.push = function(...args){
// //默认操作
// push.apply(this,[...args]);
// //通知依赖notify
// this._dep && this._dep.notify();
// }
//引用计算属性方法
let computedValue = computed(() => count.value + 3)
//定义watch
watch(() => count.value, (newValue,oldValue) => {
console.log(newValue,oldValue)
},immediate)
//首先把x改为应用类型
x = ref(1);
watchEffect(() => {
y = f(x.value);
console.log(y);
console.log(computedValue.value);
});
x.value = 2;
x.value = 3;
x.value = 4;
// 打印出 200 500
//1. let x = computed(() => count.value + 3)
//2. watch('obj.lily',(currentValue,preValue) => {
//},{deep,immediate})
//3. let stop = watchEffect(() => count.value + 3 )
其实改完后不难发现,Set不需要了,数组api代理不需要了,这也是一个比较大的区别,看起来也不会太蠢。