• vue3中递归监听和非递归监听(系列七)


    递归监听

    默认情况下,Vue3 中的 ref 和 reactive 都是递归监听的(层级深的对象),即能实时监听对象的底层变化。

    例如,在 ref 中

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { ref } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = ref({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          msg.value.a.b.c = 'C';
          msg.value.e.f = 'F';
          msg.value.g = 'G';
        };
        return {
          msg,
          c
        };
      }
    }
    </script>
     

    0JY0MT.png

    点击 button

    0JYUGq.png

    reactive递归

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { reactive } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = reactive({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          msg.a.b.c = 'C';
          msg.e.f = 'F';
          msg.g = 'G';
        };
        return {
          msg,
          c
        };
      }
    }
    </script>

    在 reactive 中也是类似的。总之,就是只要我们对 ref 和 reactive 中的内容进行更改,都是能察觉到并且进行双向数据绑定的。

    在默认情况下,递归监听肯定是好的,它让数据的变化能被实时监测到。然而它也带来了性能消耗的问题。

    Vue3 提供了 shallow 方案,以防止进行递归式的监听。

    非递归监听

      1.递归监听存在的问题
      如果数据量比较大, 非常消耗性能
    
      2.非递归监听
      shallowRef / shallowReactive
    
      3.如何触发非递归监听属性更新界面?
      如果是shallowRef类型数据, 可以通过triggerRef来触发
        注意点: 如果是通过shallowRef创建数据,
         那么Vue监听的是.value的变化, 并不是第一层的变化
    4.应用场景
      一般情况下我们使用 ref和reactive即可
      只有在需要监听的数据量比较大的时候, 我们才使用shallowRef/shallowReactive

    #shallow

    #shallowRef

    shallow 式的创建 ref 需要使用一个新的 api,shallowRef

    尝试用 shallowRef 对我们一开始的示例进行改造。

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { ref, shallowRef } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = shallowRef({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          msg.value.a.b.c = 'C';
          msg.value.e.f = 'F';
          msg.value.g = 'G';
        };
        return {
          msg,
          c
        };
      }
    }
    </script>
     

    此时我们再点击 button ,会发现控制台提示了数据的改变,但并没有实现对界面的数据绑定。

    0JYaR0.png

    shallow类型的数据,只会监听最外层的数据的变化,才会引起视图层的变化此时shaollowRef类型最外层的数据是value(并不是.g),所以只有在直接改变 msg.value 的时候才会产生监测,

    例如

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { ref, shallowRef } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = shallowRef({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          // msg.value.a.b.c = 'C';
          // msg.value.e.f = 'F';
          // msg.value.g = 'G';
          msg.value = {
            a: {
              b: {
                c: 'C'
              }
            },
            e: {
              f: 'F'
            },
            g: 'G'
          }
          console.log(msg);
        };
        return {
          msg,
          c
        };
      }
    }
    </script>

    此时最外层value的数据改变(被监听到),视图ui才会发生变化

     

    0JYdzV.png

    #triggerRef

    除此之外,对于 shallow 过的 ref 对象,我们还可以手动去触发 ref 的变化监听来实现界面的改变。

    使用的 api 是 triggerRef

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { ref, shallowRef, triggerRef } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = shallowRef({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          msg.value.a.b.c = 'C';
          msg.value.e.f = 'F';
          msg.value.g = 'G';
          triggerRef(msg);
          console.log(msg);
        };
        return {
          msg,
          c
        };
      }
    }
    </script>

    此时不需要更改最外层value的数据,内层的数据发生的变化,也同样被监听到,触发视图跟新

     

    #shallowReactive

    同样的,我们还有 shallowReactive 来实现类似 shallowRef 的功能。

    <template>
    <div>
      <p>msg.a.b.c = {{msg.a.b.c}}</p>
      <p>msg.e.f = {{msg.e.f}}</p>
      <p>msg.g = {{msg.g}}</p>
      <button @click="c">button</button>
    </div>
    </template>
    
    <script>
    import { shallowReactive } from 'vue'
    export default {
      name: 'App',
      setup() {
        let msg = shallowReactive({
          a: {
            b: {
              c: 'c'
            }
          },
          e: {
            f: 'f'
          },
          g: 'g'
        });
        function c() {
          console.log(msg);
          msg.a.b.c = 'C';
          msg.e.f = 'F';
          msg.g = 'G';
          console.log(msg);
        };
        return {
          msg,
          c
        };
      }
    }
    </script>
     

    但如果你有进行实践的话会发现,这段代码仍然会允许你在点击 button 的时候对界面 UI 进行改变。

    0JYBsU.png

    原因很简单,就是我在上文提到的,shallow 会监测最外层的变化而请求更新视图层,之前在 shallowRef 中的最外层是 value ,所以我们只能改变整个 value 值来提醒变化,而这里 shallowReactive 的最外层变成了 a、 eg而上面的代码改变了 msg.g,所以引起了变化,如果我们将函数 c 的代码改成

    msg.a.b.c = 'C';
    msg.e.f = 'F';
    // msg.g = 'G';
     

    这将不会引起 视图层 的变化。(因为最外层msg.g数据并没有发生变化,不会触发视图层的更新)

    0JYsZ4.png

    #triggerReactive

    在 shallowReactive 中,并没有提供 trigger 方案来主动唤醒监测变化。

    #总结

    本质上,shallowRef 是特殊的 shallowReactive,而 ref 是特殊的 reactive。明白了这一点,理解两者的异同就会简单许多。

        // ref -> reactive
        // ref(10) ->  reactive({value:10})
        // shallowRef ->  shallowReactive
        // shallowRef(10)  ->  shallowReactive({value: 10})
        // 所以如果是通过shallowRef创建的数据, 它监听的是.value的变化
        // 因为底层本质上value才是第一层
  • 相关阅读:
    POJ1995 ZOJ2150 Raising Modulo Numbers【快速模幂】
    POJ3641 UVA11287 HDU1905 Pseudoprime numbers【素数判定+快速模幂】
    ACM题解系列之三:秋田拓哉:《挑战程序设计竞赛》(第2版)
    HDU3257 Hello World!【打印图案+位运算】
    ACM题解系列之二:刘汝佳:《算法竞赛入门经典训练指南》
    UVa10881 Piotr's Ants【模拟】
    POJ1852 UVa10714 Ants【水题】
    剑指Offer——平衡二叉树
    剑指Offer——二叉树的深度
    剑指Offer——数字在排序数组中出现的次数
  • 原文地址:https://www.cnblogs.com/fsg6/p/14484519.html
Copyright © 2020-2023  润新知