笔记Vue7-v3版本实现响应式

2次阅读

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

Vue3.0 响应式由 Object.defineProperty 改为 Proxy 实现
因为前者无奈监听到对象上增删属性的变动

Vue3.0 的响应式用到了 Proxy 和 Reflect 两个 ES6 新增的性能

Proxy

Proxy 能够了解成,在指标对象之前架设一层“拦挡”,外界对该对象的拜访,都必须先通过这层拦挡,因而提供了一种机制,能够对外界的拜访进行过滤和改写。

Reflect

Reflect对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目标有这样几个。

  1. Object 对象的一些显著属于语言外部的办法(比方 Object.defineProperty),放到Reflect 对象上。
  2. 批改某些 Object 办法的返回后果,让其变得更正当。比方,Object.defineProperty(obj, name, desc)在无奈定义属性时,会抛出一个谬误,而 Reflect.defineProperty(obj, name, desc) 则会返回false
  • Reflect.get(target, name, receiver)
    Reflect.get办法查找并返回 target 对象的 name 属性,如果没有该属性,则返回undefined
  • Reflect.set(target, name, value, receiver)
    Reflect.set办法设置 target 对象的 name 属性等于value

createReactive函数中改为应用 Proxy

let createReactive = (target, prop, value) => {target._dep = new Dep()
    return new Proxy(target, {get(target, prop) {target._dep.depend()
        return Reflect.get(target, prop)
      },
      set(target, prop, value) {target._dep.notify()
        return Reflect.set(target, prop, value)
      }
    })
  }

示例:

  1. 间接监听对象新增的属性
  2. 去掉 push 办法的解决,间接应用

都能够胜利

<!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>
  <hr>
  <button id="addArr">addArr</button>
  <div id="appArr"></div>
</body>
<script>
  let active

  let effect = (fn, options = {}) => {
    // 为什么要减少一个_effect 函数
    // 因为须要给_effect 减少属性
    // 也能够间接给 fn 减少,然而因为援用类型的起因,会对 fn 函数造成净化
    let _effect = (...args) => {
      try {
        active = _effect
        return fn(...args)
      } finally {active = null}
    }

    _effect.options = options
    _effect.deps = [] // effect 和 dep 的关系 -1
    return _effect
  }

  let cleanUpEffect = (effect) => {
    // 革除依赖
    // 须要反向查找 effect 被哪些 dep 依赖了
    // 在 effect 上增加[] 建设双向索引
    const {deps} = effect
    console.log(deps)
    console.log(effect)
    if (deps.length) {for (let i = 0; i < deps.length; i++) {deps[i].delete(effect)
      }
    }
  }

  let watchEffect = (cb) => {
    /* active = cb
    active()
    active = null */
    let runner = effect(cb)
    runner()

    return () => {cleanUpEffect(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)
        active.deps.push(this.deps) // effect 和 dep 的关系 -2
      }
    }
    // 触发
    notify() {this.deps.forEach(dep => queueJob(dep))
      this.deps.forEach(dep => {dep.options && dep.options.schedular && dep.options.schedular()
      })
    }
  }

  let createReactive = (target, prop, value) => {target._dep = new Dep()
    return new Proxy(target, {get(target, prop) {target._dep.depend()
        return Reflect.get(target, prop)
      },
      set(target, prop, value) {target._dep.notify()
        return Reflect.set(target, prop, value)
      }
    })
  }

  let ref = (initValue) => createReactive({}, 'value', initValue)

  const set = (target, prop, initValue) => createReactive(target, prop, initValue)

  let computed = (fn) => {
    let value
    let dirty = true // 为 true 表明依赖的变量产生了变动,此时须要从新计算
    let runner = effect(fn, {schedular() {if (!dirty) {dirty = true}
      }
    })
    return {get value() {if (dirty) {
          // 何时将 dirty 重置为 true,当执行 fn 后
          // 因而须要通过配置回调函数,在执行 fn 后将 dirty 重置为 true
          // value = fn() 
          value = runner()
          dirty = false
        }
        return value
      }
    }
  }

  let watch = (source, cb, options = {}) => {const { immediate} = options
    const getter = () => {return source()
    }
    // 将函数增加到 count 的依赖下来,当 count 变动时
    let oldValue
    const runner = effect(getter, {schedular: () => applyCb()})

    const applyCb = () => {let newValue = runner()
      if (newValue !== oldValue) {cb(newValue, oldValue)
        oldValue = newValue
      }
    }

    if (immediate) {applyCb()
    } else {oldValue = runner()
    }
  }

  // set 示例:let count = ref(0)
  // count.v 新增属性
  document.getElementById('add').addEventListener('click', function () {if (!count.v) {count.v = 0}
    count.v++
  })

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

  // 数组 push 示例:let arrValue = 0
  // set 函数中曾经对依赖进行了一次增加
  let countArr = set([], 1, 0)
  document.getElementById('addArr').addEventListener('click', function () {
    arrValue++
    countArr.push(arrValue)
  })
  watchEffect(() => {str = `hello ${countArr.join(',')}`
    document.getElementById('appArr').innerText = str
  })

</script>

</html>
正文完
 0