Vue3.0响应式原理分析(2.0演变为3.0中的响应式区别)

接本人之前的一篇关于Vue2.0文章,可移步先浏览一遍,再阅读本章,效果更佳,理解更清晰。

点击下方立即查看~

Vue2.0的响应式原理+异步更新队列的实现+computed分析+watch分析https://blog.csdn.net/shi_Avicii/article/details/105895227https://blog.csdn.net/shi_Avicii/article/details/105895227

众所周知,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代理不需要了,这也是一个比较大的区别,看起来也不会太蠢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值