zoukankan      html  css  js  c++  java
  • props 子组件更改父组件引用类型的问题

    1. 传递引用类型引发的问题

    vue开发中,我们向子组件传递一个引用类数据[Array/Object]作为子组件的初始值 ,

    现在我的结构是这样的 父组件(father)下面有两个子组件child1,child2

    在通过props把数组类型同一个值传递给child1,child2,当我们通过child2来更改props的值时(原则上子组件不可以改变父组件传递过来的props值,如果时基本类型会报错),更改后父组件也会相应变化,父组件变化了相应的子组件也发生了变化,所以当点击child2修改值时,把father组件的值更改了,从而也改变了child1的值,下面会讲到如何来解决这个问题?

    父组件father

    <template>
       <div>
          <h2>孩子1</h2>
          <child1 :testArr="testObj.testArr"></child1>
          <hr>
          <h2>孩子2</h2>
          <child2 :testArr="testObj.testArr"></child2>
    
          <hr>
    
       </div>
    </template>
    
    <script>
    import Child1 from './demo/Child1'
    import Child2 from './demo/Child2'
    
    export default {
      data(){
        return {
          testObj:{
            require:true,
            testArr:[
              {
                id:1,
                name:"张三"
              },
               {
                id:2,
                name:"赵四"
              }
            ]
          }
        }
      },
      components:{
        Child1,
        Child2
      }
    }
    </script>
    
    <style>
    
    </style>
    

    子组件Child1

    <template>
       <div class="child1">
           <div v-for="(item,index) in testArr" :key="index">{{item.id}}---{{item.name}}</div>
       </div>
    </template>
    
    <script>
    export default {
      props:{
          testArr:Array
      }
    }
    </script>
    
    <style>
    
    </style>
    

    子组件child2

    <template>
       <div class="child2">
           <div v-for="(item,index) in testArr" :key="index">{{item.id}}---{{item.name}}</div>
           <button @click="changeName">点击改变数组第一个的值</button>
       </div>
    </template>
    
    <script>
    export default {
      props:{
          testArr:Array
      },
      methods:{
          changeName() {
              this.testArr[1].name="花花";
          }
      }
    }
    </script>
    
    <style>
    
    </style>
    

    当点击child2修改值时,把father组件的值更改了,从而也改变了child1的值

    怎么解决这个问题?

    可以把props传过来的数据保存在data里面,注意要深拷贝

    错误写法

     data(){
          return {
              testData:this.testArr
          }
      }
    

    深拷贝代码参考

    // 递归实现一个深拷贝
    function deepClone(source) {
      if (!source || typeof source !== 'object') {
        throw new Error('error arguments', 'shallowClone');
      }
      var targetObj = source.constructor === Array ? [] : {};
      for (var keys in source) {
        if (source.hasOwnProperty(keys)) {
          if (source[keys] && typeof source[keys] === 'object') {
            targetObj[keys] = source[keys].constructor === Array ? [] : {};
            targetObj[keys] = deepClone(source[keys]);
          } else {
            targetObj[keys] = source[keys];
          }
        }
      }
      return targetObj;
    }
      
    

    但是这种情况下父组件改变参数时,子组件无法更新参数,需要时可以通过watch或者computed来实现实时更新

    先来一种不是深拷贝的赋值,错误写法

    注意:用watch监听时,第一次时不会执行的,后面值改变了才会执行,所以要换一种写法

      data() {
        return {
            testData:[]
        }
      },
    
      watch:{
          testArr(newValue){
              this.testData=newValue
          } 
      }
    

    不是深拷贝的赋值,正确写法

    watch: {
        testArr: {
          handler(newValue, lodValue) {
            this.testData = newValue;
          },
          immediate: true  //代表在watch生命了testArr这个方法之后会立即去执行handler方法,自己理解,初次改变会执行这个方法
        }
      }
    

    上面的这种写法 还是会出现更改了child2从而改变child1的值,刚说的用深拷贝就不会出现这种问题了

    深拷贝错误写法

    报错:vue.runtime.esm.js?2b0e:1888 ReferenceError: deepClone is not defined

    当watch监听到testArr变化时,可能method里面的方法我们还访问不到,所以不要写在method方法里面,我们可以在外面定义一个文件,然后在里面引入

      watch: {
        testArr: {
          handler(newValue, lodValue) {
            this.testData = this.deepClone(newValue);
          },
          immediate: true  //代表在watch生命了testArr这个方法之后会立即去执行handler方法,自己理解,初次改变会执行这个方法
        }
      },
      methods: {
        changeName() {
          this.testData[1].name = "花花";
        },
        // 递归实现一个深拷贝
        deepClone(source) {
          if (!source || typeof source !== "object") {
            throw new Error("error arguments", "shallowClone");
          }
          var targetObj = source.constructor === Array ? [] : {};
          for (var keys in source) {
            if (source.hasOwnProperty(keys)) {
              if (source[keys] && typeof source[keys] === "object") {
                targetObj[keys] = source[keys].constructor === Array ? [] : {};
                targetObj[keys] = deepClone(source[keys]);
              } else {
                targetObj[keys] = source[keys];
              }
            }
          }
          return targetObj;
        }
      }
    

    正确写法

    utils.js文件

    export function  deepClone(source) {
        if (!source || typeof source !== "object") {
          throw new Error("error arguments", "shallowClone");
        }
        var targetObj = source.constructor === Array ? [] : {};
        for (var keys in source) {
          if (source.hasOwnProperty(keys)) {
            if (source[keys] && typeof source[keys] === "object") {
              targetObj[keys] = source[keys].constructor === Array ? [] : {};
              targetObj[keys] = deepClone(source[keys]);
            } else {
              targetObj[keys] = source[keys];
            }
          }
        }
        return targetObj;
      }
    

    在需要用到函数的地方引入,然后直接用就可以了

    import {deepClone} from './demo/utils.js'
    

    child2最后代码

    <template>
      <div class="child2">
        <div v-for="(item,index) in testData" :key="index">{{item.id}}---{{item.name}}</div>
        <button @click="changeName">点击改变数组第一个的值</button>
      </div>
    </template>
    
    <script>
    import { deepClone } from "./utils.js";
    export default {
      props: {
        testArr: Array
      },
      data() {
        return {
          testData: []
        };
      },
    
      watch: {
        testArr: {
          handler(newValue, lodValue) {
            this.testData = deepClone(newValue);
          },
          immediate: true  //代表在watch生命了testArr这个方法之后会立即去执行handler方法,自己理解,初次改变会执行这个方法
        }
      },
      methods: {
        changeName() {
          this.testData[1].name = "花花";
        }
        // 递归实现一个深拷贝
     
      }
    };
    </script>
    
    <style>
    </style>
    

    现在点击child2里面的按钮,更改数据,就不会改到child1的数据了

  • 相关阅读:
    RMAN 高级恢复
    从问题域出发认识Hadoop生态系统
    下一代 Hadoop YARN :相比于MRv1,YARN的优势
    Sensei:分布式, 实时, 半结构化数据库
    盘点2012:云计算的春天
    Apache Tajo:一个运行在YARN上支持SQL的分布式数据仓库
    实现多个jetty实例开机后自动启动
    淘宝在数据处理领域的项目及开源产品介绍
    SenseiDB架构设计分析
    在Hadoop上运行SQL:程序员需知晓的13种数据工具
  • 原文地址:https://www.cnblogs.com/wlhappy92/p/props.html
Copyright © 2011-2022 走看看