很多情况下,我们更需要传入子组件 props 的值(也就是父组件中的值)是一个双向的,也就是说子组件更改时,父组件也伴随更改。
特别是当子组件使用了某些第三方 UI 组件库的时候,在子组件内进行了 v-model 双向绑定,而该值又需父组件传入 props 进行依赖,于是当第三方组件事件被触发导致 v-model 值发生改变,就产生了冲突,因为此时父级传入的 props 不可更改。
下面简单说明三种解决的方法。
这是官网推荐的方法,当子组件需要更改父级传入的 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 的值被更改事件,但这还需要引入其他变量写很多逻辑,令人难以理解,这不是我们期望的。
通过 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() 方法,否则不会触发视图更新。
同上 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() 方法。