yyyyy28 2023-06-26 11:22 采纳率: 0%
浏览 29

学习尚硅谷禹神b站vue3时学习vue监视原理中遇到的疑惑,

在学习尚硅谷禹神的最新的vue3时时有这样一个疑问:
就是一开始使用如下代码,在页面中读取data.name时会产生死循环:

img


然后后来使用observe构造函数创建一个obs对象,然后再把vm._data和data和obs连等,为什么这样在读取data.name时不会产生死循环,我的想法如下图:

img

我没怎么学过前端,还请各位解答谢谢。

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-06-26 14:22
    关注
    • 这篇博客: 【Vue 组件化开发 二 】注册组件的语法糖、组件模板的分离写法、组件的数据中的 2.组件的data为什么必须要是函数 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    • ​组件的思想是复用,定义组件当然是把通用的公共的东西抽出来复用。

      以下代码中定义了两个组件cpn1cpn2,都是定义了两个计数器,con1的data虽然使用了函数,但是为了模拟data:{count:0},使用了常量obj来返回count。

      <div id="app">
          <h2>data不使用函数</h2>
          <cpn1></cpn1>
          <cpn1></cpn1>
          <hr>
          <h2>data使用函数</h2>
          <cpn2></cpn2>
          <cpn2></cpn2>
          <hr>
        </div>
        <script src="../js/vue.js"></script>
        <template id="cpn1">
          <div>
            <button @click="count--">-</button>
            当前计数:{{count}}
            <button @click="count++">+</button>
          </div>
        </template>
        <template id="cpn2">
          <div>
            <button @click="count--">-</button>
            当前计数:{{count}}
            <button @click="count++">+</button>
          </div>
        </template>
        <script>
          const obj = {
            count:0
          };
          const app = new Vue({
            el: "#app",
            components: { //局部组件创建
              cpn1: {
                template: '#cpn1',
                data() {
                  return obj;
                }
              },
              cpn2: {
                template: '#cpn2',
                data() {
                  return {
                    count: 0
                  }
                }
              }
            }
          })
        </script>

       图中可以看到,不使用函数data的好像共用一个count属性,而使用函数的data的count是各自用各自的,像局部变量一样有块级作用域,这个块级就是vue组件的作用域。

      我们在复用组件的时候肯定希望,各自组件用各自的变量,如果确实需要都用一样的,可以全局组件注册,也可以是用vuex来进行状态管理。

    • 以下回答由chatgpt基于相关博客总结生成:

      针对问题: 当页面中读取data.name时产生死循环的问题该如何解决?

      解决方案: 1. 确认是因为该问题导致的死循环,可以通过console.log()等打印信息,定位代码中出现问题的位置。 2. 将组件中的data定义成函数形式,确保每个组件实例都有一个独立的数据源。 3. 使用观察者模式解决数据监听问题。可以通过创建一个Observer类,并在该类中使用Object.defineProperty()为每个属性添加getter和setter监听,从而实现数据劫持。在创建组件实例时,使用observe构造函数创建Observer对象,并将vm._data和data和obs进行连等。具体代码实现如下:

      class Observer {
        constructor(value) {
          this.value = value;
          if (!Array.isArray(value)) {
            this.walk(value);
          }
        }
      
        walk(obj) {
          const keys = Object.keys(obj);
          for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i]);
          }
        }
      }
      
      function defineReactive(obj, key, val) {
        const dep = new Dep(); //定义一个dep依赖实例
        const property = Object.getOwnPropertyDescriptor(obj, key);
        if (property && property.configurable === false) {
          return;
        }
        val = obj[key];
      
        // 继续递归核心算法,判断val是不是对象,如果是,就递归下去
        let childOb = observe(val);
        Object.defineProperty(obj, key, {
          enumerable: true,
          configurable: true,
          get: function reactiveGetter() {
            const value = property.get ? property.get.call(obj) : val;
            if (Dep.target) { // 当Dep.target存在时,添加订阅进来
              dep.depend();
              if (childOb) { // 如果子组件不为空,则将新添加的Dep对象添加进去
                childOb.dep.depend();
              }
            }
            return value
          },
          set: function reactiveSetter(newVal) {
            const value = property.get ? property.get.call(obj) : val;
            if (newVal === value || (newVal !== newVal && value !== value)) { //如果要设置的值等于现在的值,或者设置的值是NaN,并且现在的值也是NaN,则直接结束递归
              return
            }
            if (property && property.set) {
              property.set.call(obj, newVal);
            } else {
              val = newVal;
            }
            childOb = observe(newVal);
            dep.notify(); // 如果值发生了改变,则通知订阅者更新组件视图
          }
        });
      }
      
      function observe(value) {
        if (!value || typeof value !== 'object') {
          return
        }
        if (value.__ob__) {
          return value.__ob__;
        }
        return new Observer(value);
      }
      
      function initState(vm) {
        const data = vm.$options.data;
        vm._data = data.call(vm);
      
        // 执行observe函数
        observe(vm._data);
      }
      
      let vm = new Vue({
        el: "#box",
        data() {
          return {
            num: 0
          }
        },
        components: {
          temp: {
            template: "#temp",
            data() {
              return {
                num: 2
              }
            }
          }
        }
      });
      

      以上是使用observe构造函数创建obs对象声明方式来通过观察者模式解决死循环问题的代码实现。其中,Observer类是对整个数据对象的分类,递归地为对象中每个属性定义get/set方法进行数据监听。而在getter方法中,使用Dep类的depend方法来将订阅者添加到dep实例的subs队列中,将该属性与订阅者联系起来。在setter方法中,使用Dep类的notify方法,通知其subs队列中所有的订阅者进行更新操作。

      最后,对于问题“为什么要使用observe构造函数创建obs对象,并将vm._data和data和obs连等?”,这是因为observe构造函数创建的obs对象可以进行递归监听,实现对整个对象的监听,而连等可以将_data和data和obs的引用指向同一块内存地址,这样修改_data和data中的值时,都指向一个地址,可以互相影响,实现对data数据的监听。

    评论

报告相同问题?

问题事件

  • 创建了问题 6月26日

悬赏问题

  • ¥15 两台交换机分别是trunk接口和access接口为何无法通信,通信过程是如何?
  • ¥15 C语言使用vscode编码错误
  • ¥15 用KSV5转成本时,如何不生成那笔中间凭证
  • ¥20 ensp怎么配置让PC1和PC2通讯上
  • ¥50 有没有适合匹配类似图中的运动规律的图像处理算法
  • ¥15 dnat基础问题,本机发出,别人返回的包,不能命中
  • ¥15 请各位帮我看看是哪里出了问题
  • ¥15 vs2019的js智能提示
  • ¥15 关于#开发语言#的问题:FDTD建模问题图中代码没有报错,但是模型却变透明了
  • ¥15 uniapp的h5项目写一个抽奖动画