Vue源码系列6:模拟实现vue3.x中的ref

前言

本文主要对vue3.x中的ref进行模拟实现。

一. 完整代码

const reactMap = new WeakMap();
const ReactiveFlags = {
    IS_REACTIVE : "isReactive"
}

let activeEffect = undefined;
class ReactiveEffect {
    active = true;
    deps = [];
    constructor(fn,scheduler) {
        this.fn = fn;
        this.scheduler = scheduler;
    }
    run() {
        if(!this.active) {this.fn()};
        try {
            activeEffect = this;
            return this.fn()
        } finally {
            activeEffect = undefined;
        }
    }
}

// 处理对象再次代理,可以直接返回
function isReactive(value) {
    return !!(value && value[ReactiveFlags.IS_REACTIVE])
}
// 将数据转化成响应式的数据,只能做对象的代理
function reactive(target) {
    if(!(typeof target === 'object' && target !== null)) {
        return;
    }
    if(target[ReactiveFlags.IS_REACTIVE]) {
        return target
    }
    let exisProxy = reactMap.get(target);
    if(exisProxy) {
        return exisProxy
    }
    const proxy = new Proxy(target,{
        get(target,key,receiver) {
        	if(key === ReactiveFlags.IS_REACTIVE) {
        		return true
        	}
            console.log(target,'get',key)
            track(target,'get',key)
            return Reflect.get(target,key,receiver)
        },
        set(target,key,value,receiver) {
            let oldValue = target[key];
            let result = Reflect.set(target,key,value,receiver);
            if(oldValue !== value) {
                console.log(target,'set',key)
                trigger(target,'set',key,oldValue,value)
            }
            return result 
        }
    });
    reactMap.set(target,proxy)
    return proxy
}

const targetMap = new WeakMap()

function trigger(target,type,key,oldValue,value) {
    const depsMap = targetMap.get(target);
    if(!depsMap) return;
    const effects = depsMap.get(key);
    triggerEffect(effects,'effects')
}

function triggerEffect(effects) {
    effects && effects.forEach(effect => {
        if(effect.scheduler) {
            effect.scheduler()
        } else {
            effect.run()
        }
    })
}

function track(target,type,key) {
    if(!activeEffect) return ;
    let depsMap = targetMap.get(target);
    if(!depsMap) {
        targetMap.set(target,(depsMap = new Map()))
    }
    let dep = depsMap.get(key);
    if(!dep) {
        depsMap.set(key,(dep = new Set()))
    }
    trackEffect(dep)
}

function trackEffect(dep) {
    if(activeEffect) {
        let shouldTrack =!dep.has(activeEffect);
        if(shouldTrack) {
            dep.add(activeEffect)
            activeEffect.deps.push(dep);
        }
    }
}
function isObject(value) {
    return typeof value === 'object' && value !== null;
}

function isFunction(val) {
    return typeof val === 'function'
}
function isArray(val) {
    return Array.isArray(val)
}

function traversal (value, set = new Set()) {
    if(!isObject(value)) return value;
    if(set.has(value)) {
        return value
    }
    set.add(value);
    for(let key in value) {
        traversal(value[key],set);
    }
    return value
}

function toReactive(value) {
    return isObject(value)?reactive(value):value
}

class RefImpl {
    _value;
    —isRef = true
    constructor(rawValue) {
        this._value = toReactive(rawValue)
    }
    get value() {
        return this._value
    }
    set value(newValue) {
        if(newValue !== this.rawValue) {
            this._value = toReactive(newValue);
            this.rawValue = newValue
        }
    }
}

function ref(value) {
    return new RefImpl(value)
}

二. 封装思路

官网原话: reactive() 的种种限制归根结底是因为 JavaScript 没有可以作用于所有值类型的 “引用” 机制。为此,Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref。

当ref包裹的是基础数据类型时,实现响应式的原理是Object.defineProperty的getter和setter,当为复杂数据类型时,则利用proxy,原理同reactive。响应式返回的值保存在value属性中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值