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>