《Vue进阶教程》第二十四课:优化

发布于:2025-02-11 ⋅ 阅读:(29) ⋅ 点赞:(0)

  往期内容:

《Vue进阶教程》第十三课:实现依赖收集

《Vue进阶教程》第十四课:改进桶结构

《Vue进阶教程》第十五课:深入完善响应式系统之模块化

《Vue进阶教程》第十六课:深入完善响应式系统之单例模式

《Vue进阶教程》第十七课:支持分支切换

《Vue进阶教程》第十八课:避免死循环

《Vue进阶教程》第十九课:computed初步实现

《Vue进阶教程》第二十课:lazy懒执行

《Vue进阶教程》第二十一课:支持缓存

《Vue进阶教程》第二十二课:自定义更新(调度器)

《Vue进阶教程》第二十三课:渲染计算属性的结果

1) 封装

由于computed函数最终会返回一个对象.

可以考虑将返回的对象封装成ComputedRefImpl的实例

class ComputedRefImpl {
  constructor(fn) {
    this._value = null // 缓存
    this._dirty = true // 标识
    this.effect = new RectiveEffect(fn, () => {
      if (!this._dirty) this._dirty = true
      trigger(this, 'value')
    })
  }
  get value() {
    // 收集当前计算属性依赖的副作用函数
    track(obj, 'value')  
    
    if (this._dirty) {
      this._value = this.effect.run()
      this._dirty = false
    }
    return this._value
  }
}
// 这里先只考虑fn是函数的情况
function computed(fn) {
  return new ComputedRefImpl(fn)
}

2) 扩展

考虑到计算属性是可以支持两种配置的. 进一步扩展支持配置(getter/setter)

class ComputedRefImpl {
  constructor(getter, setter) {
    this._value = null // 缓存
    this._dirty = true // 标识
    this.effect = new RectiveEffect(getter, () => {
      if (!this._dirty) this._dirty = true
      trigger(this, 'value')
    })
    this.setter = setter
  }
  get value() {
    track(this, 'value')
    if (this._dirty) {
      this._value = this.effect.run()
      this._dirty = false
    }
    return this._value
  }
  set value(newVal) {
    return this.setter(newVal)
  }
}

function computed(getterOrOptions) {
  let getter
  let setter
  if (typeof getterOrOptions == 'function') {
    getter = getterOrOptions
    setter = () => {
      console.warn('no setter')
    }
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }
  return new ComputedRefImpl(getter, setter)
}

3) 解耦复用

在收集依赖时, 不能很好的观察到当前计算属性依赖的副作用函数.

可以考虑给ComputedRefImpl加一个dep属性, 用来保存哪些副作用函数引用了当前计算属性

而且, 在调用tracktrigger时, 必须要传入两个参数. 这里我们人为创造了一个value. 这种方式不够优雅

考虑对tracktrigger进一步解耦

class ComputedRefImpl {
  constructor(getter, setter) {
    this._value = null // 缓存
    this._dirty = true // 标识
    this.dep = new Set()
    this.effect = new RectiveEffect(getter, () => {
      if (!this._dirty) this._dirty = true
      triggerEffects(this.dep)
    })
    this.setter = setter
  }
  get value() {
    trackEffects(this.dep)
    if (this._dirty) {
      this._value = this.effect.run()
      this._dirty = false
    }
    return this._value
  }
  set value(newVal) {
    return this.setter(newVal)
  }
}

实现trackEffects

function trackEffects(dep) {
  // 只有当activeEffect有值时, 才需要收集依赖
  if (!activeEffect) return

  dep.add(activeEffect)
  activeEffect.deps.push(dep)
}

实现triggerEffects

function triggerEffects(dep) {
  if (!dep) return

  const effects = [...dep]
  effects.forEach(effect => {
    if (effect !== activeEffect) {
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        effect.run()
      }
    }
  })
}


网站公告

今日签到

点亮在社区的每一天
去签到