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的数据了

原文地址:https://www.cnblogs.com/wlhappy92/p/props.html