vue父子组件双向绑定数组深度监听死循环的问题

现在有个父组件和子组件需要双向绑定一个数组,但是父组件只需要用到数组元素的id,子组件用到数组元素的id和type等属性信息,我用下面两个测试组件来模拟,这样写会死循环,有没有什么解决方法?

  • 父组件
<template>
  <div>
    root_data:{{ data }}
    <test-child :data="data" @change="data = $event"></test-child>
    <el-button type="primary" @click.native="test">test</el-button>
  </div>
</template>
<script>
import TestChild from "@/views/FormConfig/TestChild";
export default {
  components: {
    TestChild
  },
  data() {
    return {
      data: []
    };
  },
  methods: {
    test() {
      this.data = [1, 2, 3];
    }
  }
};
</script>
  • 子组件
<template>
  <div>
    {{ data }}
  </div>
</template>
<script>
export default {
  props: ["data"],
  data() {
    return {
      mydata: []
    };
  },
  watch: {
    data: {
      handler(val) {
        let mydata = [];
        for (let i = 0; i < val.length; i++) {
          mydata.push({ id: val[i], type: "int" });
        }
        this.mydata = mydata;
      },
      immediate: true,
      deep: true
    },
    mydata: {
      handler(val) {
        let result = [];
        for (let i = 0; i < val.length; i++) {
          result.push(val[i].id);
        }
        console.log("emitchange result", result);
        this.$emit("change", result);
      },
      deep: true
    }
  }
};
</script>
阅读 8k
3 个回答

已经解决了,通过JSON.stringify将两个对象转成字符串对比是否一样,从而决定是否更新。

watch: {
    data: {
      handler(val) {
        let mydata = [];
        for (let i = 0; i < val.length; i++) {
          mydata.push({ id: val[i], type: "int" });
        }
        if (JSON.stringify(this.mydata) !== JSON.stringify(mydata)) {
          this.mydata = mydata;
        }
      },
      immediate: true,
      deep: true
    }
  }

因为深度监听,每次都是直接赋值,都造成了引用新的对象,一边换触发watch,造成父子无限循环;其实不必watch这样赋值,你可以直接使用计算属性。

较为简单的属性用于展示,建议用计算属性。

数据变化时执行异步或开销较大的操作时,建议使用watch。

你的可以改为:

computed: {
    mydata() {
      let newArr = []
      for (let item of this.data) {
        newArr.push({id: item, type: 'init'})
      }
      return newArr
    }
},

双向绑定的话可以这样:

// 父组件
<test-child v-model="data" @change="data = $event"></test-child>

// 子组件
export default {
  props: {
    value: {
      type: Array,
      default: function() {
        return []
      }
    }
  },
  data() {
    return {
      mydata: this.value
    };
  },
  watch: {
    value: function(newVal) {
      let newArr = newVal.map(function(data) {
        let item = {
          id: data,
          type: 'init'
        }
        return item
      })
      // 防止无限循环,简单比较一下
      if (JSON.stringify(newVal) != JSON.stringify(this.mydata)) {
        this.mydata = newArr
      }
    },
    mydata: function(newVal) {
      this.$emit('input', newVal)
    }
  }
}

可以用.sync事件修饰符作这种双向绑定

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题