zoukankan      html  css  js  c++  java
  • vue父组件data改变触发子组件prop值变化

    记录下最近发现的vue的一个小bug,或者说vue的一个小坑:

      项目中父组件引用子组件,子组件对传递过来的prop之value设置了监听,  父组件更改和prop之value无关的属性值,会触发子组件的watch;说不清楚还是看代码吧:

    // 父组件

    <template>
      <div class="home">
        <HelloWorld  :value='[1]'/>
        <div>res:{{propsData}}</div>
        <button @click="setPropData">setPropData</button>
      </div>
    </template>
    
    <script>
    import HelloWorld from '@/components/HelloWorld.vue'
    
    export default {
      name: 'Home',
      components: {
        HelloWorld
      },
      data() {
        return {
          propsData:[123],
          value:[1]
        }
      },
      methods: {
        setPropData(){
          this.propsData=[12312333]
        }
      },
    }
    </script>

    // 子组件

    <template>
      <div class="hello">
        <p>{{value}}</p>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      props: {
        value:{
          type: Array,
          default () {
            return []
          }
        },
      },
      watch: {
        value: {
          deep: true,
          handler (val, oldVal) {
            console.log('val :>> ', val)
            console.log('oldVal :>> ', oldVal)
            console.log('val === oldVal :>> ', val == oldVal)
          }
        }
      },
    }
    </script>

    其中子组件监听了传过来的value, 父组件传过来了一个数组, 这时候父组件响应点击事件,设置其他的值会触发子组件的watch,这肯定不是咱们预想的效果,因为监听value的值要去更新子组件的状态,总不能父组件任何一个属性变化我都更新下子组件的状态,咋办呢?

    尝试: 子组件watch中判断新值和旧值是否相同,如果不相同就做更新操作,当然==是不能满足要求的,看我比较的方法吧:

    isObjectValueEqual (a, b) {
        // 判断两个对象是否指向同一内存,指向同一内存返回true
          if (a === b) return true
          // 获取两个对象键值数组
          const aProps = Object.getOwnPropertyNames(a)
          const bProps = Object.getOwnPropertyNames(b)
          // 判断两个对象键值数组长度是否一致,不一致返回false
          if (aProps.length !== bProps.length) return false
          // 遍历对象的键值
          for (const prop in a) {
          // 判断a的键值,在b中是否存在,不存在,返回false
            if (Object.prototype.hasOwnProperty.call(b, prop)) {
            // 判断a的键值是否为对象,是则递归,不是对象直接判断键值是否相等,不相等返回false
              if (typeof a[prop] === 'object') {
                if (!isObjectValueEqual(a[prop], b[prop])) return false
              } else if (a[prop] !== b[prop]) {
                return false
              }
            } else {
              return false
            }
          }
          return true
        },

    题外再多说一嘴:b.hasOwnProperty(prop)这样用eslint不会通过,因为如果对象b有一个属性刚好叫hasOwnProperty,而不是方法,那就报错了,所以保险起见用call实现,这也是个面试题呀!

    这样貌似ok了吧,但还是有问题,例如给子组件传的是[1],子组件状态改成1对应的,用户滑动改变子组件的状态,这时候父组件重新传入[1],目的是让子组件归为,这时候通过上边的这个方法判断,值没有变,就不会继续操作,所以引入了一个新的bug, 改bug最操蛋的就是改一个小bug引出一个大bug,分析发现这个大bug还避免不了,咋办呢?

    回到开始的位置,父组件传值时候换个方式:不直接传数组,传data中定义的对象,如下:

    这样就不会触发了, 但还是没有解决最初的那个问题

    这个问题出现的原因,翻了半天的vue源码也没有找到咋回事,---猜想可能是对象地址引用的问题,直接传[1]每次更改视图时候,传给子组件的是一个新的[1],换成传递data中数组对象的方式后,每次都是传递的对象的引用地址而不是一个新数组对象,所以不会触发watch.如果直接传递{a:'1'}这样的话应该是一样的效果.

    over!

  • 相关阅读:
    【Solr】copy字段的应用
    【Solr】Solr的安装部署
    【UEditor】 UEditor整合项目上传资源到阿里云服务器
    【Bootstrap】Bootstrap和Java分页-第二篇
    【Bootstrap】Bootstrap和Java分页-第一篇
    工作那点小事
    ubuntu安装mongodb
    mybatis插入返回主键
    linux查看端口占用命令
    ubuntu安装ssh
  • 原文地址:https://www.cnblogs.com/rainbowLover/p/13844995.html
Copyright © 2011-2022 走看看