Vue2在子组件改变父组件传入props值三种方法(利用Vue的设计漏洞实现双向同步)

前言

很多情况下,我们更需要传入子组件 props 的值(也就是父组件中的值)是一个双向的,也就是说子组件更改时,父组件也伴随更改。

特别是当子组件使用了某些第三方 UI 组件库的时候,在子组件内进行了 v-model 双向绑定,而该值又需父组件传入 props 进行依赖,于是当第三方组件事件被触发导致 v-model 值发生改变,就产生了冲突,因为此时父级传入的 props 不可更改。

下面简单说明三种解决的方法。

v-bind:sync

这是官网推荐的方法,当子组件需要更改父级传入的 props 时,调用 this.$emit() 即可。

example

父组件 App.vue

<template>
  <div id="app">
    <Child :text.sync="text"></Child>
  </div>
</template>

<script>
import Child from "@/components/Child";
export default {
  name: "App",
  components: {
    Child
  },
  data() {
    return {
      text: 123
    };
  }
};
</script>

子组件 Child.vue

<template>
  <div>
    {{ text }}
  </div>
</template>

<script>
export default {
  props: {
    text: {
      type: [Number, String]
    }
  },
  mounted() {
    // × 当使用 .sync 时不能对父组件传来的 props 直接赋值
    // this.text = "111";
    
    // √ 需要使用 this.$emit 进行更新父组件传入的 props
    this.$emit("update:text", "111");
  }
};
</script>

官网关于 .sync 的更多介绍:.sync 修饰符

缺点

正如前言所说,假如我们使用了第三方的组件库,他的 v-model 改变是不受控的,类似与下面这种情况:

<template>
  <div>
    <input type="text" v-model="text" />
  </div>
</template>

此时我们父组件传入的 text 会被 v-model 自动更改导致报错(不能直接更改),这是一个很大的麻烦。

注:虽然组件库都有 v-model 的值被更改事件,但这还需要引入其他变量写很多逻辑,令人难以理解,这不是我们期望的。

Object props

通过 Vue2 的设计漏洞——对 object 和 array 内的值变更不做监测,我们直接将 props 传入 object 即可。

父组件 App.vue

<template>
  <div id="app">
    <Child :obj="obj"></Child>
  </div>
</template>

<script>
import Child from "@/components/Child";
export default {
  name: "App",
  components: {
    Child
  },
  data() {
    return {
      obj: { key: "value" }
    };
  }
};
</script>

子组件 Child.vue

<template>
  <div>
    <!-- 不会报错,正常使用 -->
    <input type="text" v-model="obj.key" />
    {{ obj }}
  </div>
</template>

<script>
export default {
  props: {
    obj: {
      type: Object
    }
  },
  mounted() {
  	// 同步支持设定新属性
  	this.obj.key2 = 'value2'
  	// 异步必须使用 Vue.$set
    setTimeout(() => {
      this.$set(this.obj, "key3", "value3");
    }, 3000);
  }
};
</script>

我们需要绑定的就是 object 的某个值,当 object 内的某个键值被改变,不会产生报错,即使在 v-model 双向绑定下也可正常使用。

特别需要注意的是,当进行异步操作时需要使用 $set() 方法,否则不会触发视图更新。

Array props

同上 object props 所说,array 的某一项值改变也不会被 Vue2 监测到。

父组件 App.vue

<template>
  <div id="app">
    <Child :array="array"></Child>
  </div>
</template>

<script>
import Child from "@/components/Child";
export default {
  name: "App",
  components: {
    Child
  },
  data() {
    return {
      array: [1, 2, 3]
    };
  }
};
</script>

子组件 Child.vue

<template>
  <div>
    <input type="text" v-model="array[0]" />
    {{ array }}
  </div>
</template>

<script>
export default {
  props: {
    array: {
      type: Array
    }
  },
  mounted() {
   	// 同步支持设定新属性
  	this.array[1] = 'value2'
  	// 异步必须使用 Vue.$set
    setTimeout(() => {
      this.$set(this.array, 2, "value3");
    }, 3000);
  }
};
</script>

总结

综上来看,在父子组件的数据流中,建议将可能会双向同步更新的数据放入一个 object 内,务必注意当异步时需要使用 $set() 方法。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值