美文网首页
Vue3响应式原理傻瓜式教程(一)——Reactive

Vue3响应式原理傻瓜式教程(一)——Reactive

作者: DebraJohn | 来源:发表于2021-06-11 17:48 被阅读0次

    参考教程:Vue Mastery

    理解响应式

    什么是响应式呢?举个简单的例子。

    let price = 5;
    let quantity = 2;
    let total = price * quantity; // 10
    

    现在我们将price改为6,期望total值也能自动计算更新为12。
    而这个自动计算更新,就是响应式。
    这一节我们先来谈论一下,如何触发计算更新。

    如何实现更新计算结果

    let price = 5;
    let quantity = 2;
    
    let dep = new Set() // 相当于收集方法(依赖)的仓库
    
    let effect = () => { // 计算方法
      total = price * qunatity
    }
    function track() { // 添加计算方法到仓库中
      dep.add(effect)
    }
    function trigger() { // 触发仓库中的方法
      dep.forEach(effect => effect())
    }
    

    第一步,执行effect得到初始的结果

    effect() // total = 10
    

    第二步,我们需要把effect收集起来,以便于需要的时候再次调用。

    track()
    

    第三步,当price发生改变时,再次触发已收集的方法。

    price = 6 // 此时total还是10
    trigger() // 此时得到total = 12
    

    多个属性怎么办?

    上面我们实现了一个计算方法的存储。
    实际上,每个对象会有多个属性。
    而每个属性,也都有它们各自的dep,来存储一个或多个effect。

    let product = { price: 5, quantity: 2 }
    

    所以我们建立起一个大的依赖仓库:depsMap

    const depsMap = new Map()
    

    depsMap中,用属性名(如pricequnatity)作为key,用各自的dep作为value
    那么,track方法应该这样写:

    function track(key) {
      let dep = depsMap.get(key)
      if (!dep) { // 找不到就建一个
        depsMap.set(key, (dep = new Set()))
      }
      dep.add(effect)
    }
    

    同时,trigger方法也要改写啦:

    function trigger(key) {
      let dep = depsMap.get(key)
      if (dep) {
        dep.forEach(effect => effect())
      }
    }
    

    来运行一下吧:

    let product = { price: 5, quantity: 2 }
    let total = 0
    
    let effect = () => { 
      total = product.price * product.quantity
    }
    
    effect() // total = 10
    track('price')
    
    product.price = 6 // total = 10
    trigger('price') // total = 12
    
    

    多个对象怎么办?

    上面我们实现了一个对象的响应,接下来我们来探讨下多个对象的收集方法。
    我们需要创建一个更大的仓库来存储多个object,每个object都有属于自己的depsMap。

    const targetMap = new WeakMap()
    // 为什么用WeakMap我们暂时不讨论,目前只需要知道,它的key必须是object类型
    
    function track(target, key) {
      let depsMap = targetMap.get(target)
      if (!depsMap) {  // 找不到就建一个
        targetMap.set(target, (depsMap = new Map()))
      }
      let dep = depsMap.get(key)
      if (!dep) {
        depsMap.set(key, (dep = new Set()))
      }
      dep.add(effect)
    }
    
    function trigger(target, key) {
      const depsMap = targetMap.get(target)
      if (!depsMap) return // 如果没有任何依赖,直接返回
      let dep = depsMap.get(key)
      if (dep) {
        dep.forEach(effect => effect())
      }
    }
    

    来运行一下:

    let product = { price: 5, quantity: 2 }
    let total = 0
    const depsMap = new Map()
    let effect = () => { 
      total = product.price * product.quantity
    }
    
    effect() // total = 10
    track('product', 'price')
    
    product.price = 6  // total = 10
    trigger('product', 'price') // total = 12
    

    总结

    • targetMap
      用于存放每个响应式object(所有属性)的依赖
    • depsMap
      用于存放响应式object每个属性对应的依赖
    • dep
      用于存放某个属性对应的所有依赖,当属性发生变化时,会执行依赖


      image.png

    相关文章

      网友评论

          本文标题:Vue3响应式原理傻瓜式教程(一)——Reactive

          本文链接:https://www.haomeiwen.com/subject/vvyveltx.html