笔记Vue4computed分析

3次阅读

共计 4039 个字符,预计需要花费 11 分钟才能阅读完成。

computed 的应用,传入一个函数:
let x = computed(() => count.value + 3)

新增 computed 函数

  1. 减少一个 computed 函数,传入一个函数 fn,返回一个蕴含可监听 value 值的对象
  2. value 值置为传入函数执行的后果,再将 value 返回
  3. 因为须要实现缓存,减少 dirty 标记记录依赖的值是否变动,只有变动了才从新计算
  4. 计算赋值之后将标记置为 false。只有数据没有变动,就不会再从新计算。
  5. 何时将 dirty 重置为 true?在执行传入函数 fn 时,监听的响应式数据变动之后将 dirty 重置为 true。
  6. 就是在执行 notify()时,将所有的依赖进行一次播送,将工作退出队列,之后执行。notify 中的 dep 对应的为 watchEffect 传入的 cb,因而须要革新 watchEffect。
  // 传入一个函数,返回一个蕴含可监听 value 值的对象
  let computed = (fn) => {
    let value
    // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算
    let dirty = true
    return {get value() {if (dirty) {
          // 何时将 dirty 重置为 true
          // 在执行 fn 时,监听的响应式数据变动之后将 dirty 重置为 true
          // 就是在执行 notify()时
          // notify 中的 dep 对应的为 watchEffect 传入的 cb,因而须要革新 watchEffect
          value = fn() // value 值置为传入函数执行的后果
          // 计算之后将标记置为 false。只有数据没有变动,就不会再从新计算
          dirty = false
        }
        return value
      }
    }
  }

革新 watchEffect

  1. 新增 effect 函数,将原来 watchEffect 中的内容放进去
  2. effect 中新建一个 _effect 函数,将 fn 额定包装了一层,用于给它增加属性,为了保障 fn 函数的纯正性
  let active

  let effect = (fn, options = {}) => {
    // _effect 额定包装了一层,用于给它增加属性
    // 为了保障 fn 函数的纯正性
    let _effect = (...args) => {
      try {
        active = _effect
        return fn(...args) // 须要增加 return 语句用于 computed 函数中拿到变动之后的值
      } finally {
        // 无论是否抛出异样最初 finally 都会执行
        // 这句代码是在 `return fn(...args)` 后须要执行,因而须要放进 try{}finally{}中
        active = null
      }
    }
    _effect.options = options
    return _effect
  }

  // 之前的 watch 实现的即是 watchEffect 函数的性能
  let watchEffect = (cb) => {
    /* active = cb
    active()
    active = null */
    // 将原来局部的逻辑提取到 effect 函数中
    let runner = effect(cb)
    runner()}

notify函数中触发

  1. computed 函数中须要用 effect 去代替 fn,这样能够增加钩子函数,即传入 effect 中的 options 参数
  2. notify 中执行钩子函数
  // 传入一个函数,返回一个蕴含可监听 value 值的对象
  let computed = (fn) => {
    let value
    // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算
    let dirty = true
    let runner = effect(fn, {schedular() {if (!dirty) {dirty = true}
      }
    })
    return {get value() {if (dirty) {
          // 何时将 dirty 重置为 true
          // 在执行 fn 时,监听的响应式数据变动之后将 dirty 重置为 true
          // 就是在执行 notify()时
          // notify 中的 dep 对应的为 watchEffect 传入的 cb,因而须要革新 watchEffect
          // value = fn() // value 值置为传入函数执行的后果
          value = runner()
          // 计算之后将标记置为 false。只有数据没有变动,就不会再从新计算
          dirty = false
        }
        return value
      }
    }
  }

  let ref = initValue => {
    let value = initValue
    let dep = new Dep()
    return Object.defineProperty({}, 'value', {get() {dep.depend()
        return value
      },
      set(newValue) {
        value = newValue
        // active()
        dep.notify()}
    })
  }
  // 触发
    notify() {
      this.deps.forEach(dep => {queueJob(dep)
        // 执行钩子函数
        dep.options && dep.options.schedular && dep.options.schedular()})
    }

残缺代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button id="add">add</button>
  <div id="app"></div>
</body>
<script>
  let active

  let effect = (fn, options = {}) => {
    // _effect 额定包装了一层,用于给它增加属性
    // 为了保障 fn 函数的纯正性
    let _effect = (...args) => {
      try {
        active = _effect
        return fn(...args) // 须要增加 return 语句用于 computed 函数中拿到变动之后的值
      } finally {
        // 无论是否抛出异样最初 finally 都会执行
        // 这句代码是在 `return fn(...args)` 后须要执行,因而须要放进 try{}finally{}中
        active = null
      }
    }
    _effect.options = options
    return _effect
  }

  // 之前的 watch 实现的即是 watchEffect 函数的性能
  let watchEffect = (cb) => {
    /* active = cb
    active()
    active = null */
    // 将原来局部的逻辑提取到 effect 函数中
    let runner = effect(cb)
    runner()}

  let nextTick = (cb) => Promise.resolve().then(cb)

  // 队列
  let queue = []

  // 增加队列
  let queueJob = (job) => {if (!queue.includes(job)) {queue.push(job)
      // 增加之后,将执行放到异步工作中
      nextTick(flushJob)
    }
  }

  // 执行队列
  let flushJob = () => {while (queue.length > 0) {let job = queue.shift()
      job && job()}
  }


  let Dep = class {constructor() {
      // 寄存收集的 active
      this.deps = new Set()}
    // 依赖收集
    depend() {if (active) {this.deps.add(active)
      }
    }
    // 触发
    notify() {
      this.deps.forEach(dep => {queueJob(dep)
        // 执行钩子函数
        dep.options && dep.options.schedular && dep.options.schedular()})
    }
  }

  // 传入一个函数,返回一个蕴含可监听 value 值的对象
  let computed = (fn) => {
    let value
    // 须要设置一个标记记录依赖的值是否变动,只有变动了才从新计算
    let dirty = true
    let runner = effect(fn, {schedular() {if (!dirty) {dirty = true}
      }
    })
    return {get value() {if (dirty) {
          // 何时将 dirty 重置为 true
          // 在执行 fn 时,监听的响应式数据变动之后将 dirty 重置为 true
          // 就是在执行 notify()时
          // notify 中的 dep 对应的为 watchEffect 传入的 cb,因而须要革新 watchEffect
          // value = fn() // value 值置为传入函数执行的后果
          value = runner()
          // 计算之后将标记置为 false。只有数据没有变动,就不会再从新计算
          dirty = false
        }
        return value
      }
    }
  }

  let ref = initValue => {
    let value = initValue
    let dep = new Dep()
    return Object.defineProperty({}, 'value', {get() {dep.depend()
        return value
      },
      set(newValue) {
        value = newValue
        // active()
        dep.notify()}
    })
  }

  // 应用:let count = ref(0)
  // computedValue 当 count.value 的值扭转时才变动
  let computedValue = computed(() => count.value + 3)
  document.getElementById('add').addEventListener('click', function () {count.value++})

  let str
  watchEffect(() => {str = `hello ${count.value} ${computedValue.value}`
    document.getElementById('app').innerText = str
  })

</script>

</html>
正文完
 0