04-computed&watch

97 阅读1分钟

computed

  • computed 方法的核心是基于effect
  • computed 方法返回的是一个带有.value属性的ComputeRefImp对象
const computed =(getterOrOptions) {
    let getter;
    let setter;
    if(isFunction(getterOrOptions)) {
        getter = getterOrOptions
        setter = ()=> {
            console.log('computed can not be set')
        }
    }else {
        getter = getterOptions.get
        setter = getterOrOpitons.set
    }
    
    const crf = new ComputedRefImp(getter,setter)
    return crf
}

class ComputedRefImp {
    _dirty = true;
    deps = new Set()
    effect = null
    _value = null
    construction(getter,setter) {
        this.effect = new ReacitveEffect(getter, ()=> {
            if(!this._dirty) {
                this._dirty = true
                triggerEffect(this.deps) // 执行依赖
            }
        })
    }
    
    get value () {
        trackEffects(this.deps) // 收集副作用
        if(this._dirty) {
            this._dirty = false
            this._value = this.effect.run(); // 收集副作用
        }
        
        return this._value
    }
    set value(newValue) {
        this.setter(newValue)
    }
}

watch

  • watch 方法是基于 effect

  • 参数

    • reactive 对象,会被递归遍历
    • callback 函数,里面返回一个值
  • 返回值 newValue, oldValue, onCleanup

    • 如果传入的是一个对象,那么新值和旧值无法区分,引用同一个地址
    • 如果传入是一个函数,且返回的是一个原始值,那么新值和旧值可以区分
    • onCleanup 接受一个 函数,用于在异步执行的时,对上一次的 watch callback 函数内部执行进行控制(利用闭包的原理)。
const watch = (source,callback)=> {
    let getter;
    let oldValue;
    if(isReactive(source)) {
        getter = ()=> travesal(source); // 递归的目的是为了收集响应式对象的每一层属性的副作用
    }else if(isFunction(source)) {
        getter = soruce
    }else {
        return
    }
    let cleanup;
    const onCleanup = (cb) {
        cleanup = cb
    }
    const job = ()=> {
        if(cleanup) {
            cleanup()
        }
        const newValue = this.effect.run()
        callback(newValue,oldValue,onCleanup)
        oldValue = newValue
    }
    this.effect = new ReactiveEffect(getter,job)
    oldValue = this.effect.run() // 收集副作用
}
const travesal(value,set = new Set()) {
    if(!isObject(value)) return value;
    if(set.has(value)) {
        return value
    }
    set.add(value)
    let(key in value) {
        travesal(value[key],set)
    }
    
    return value
}