前言
很多情况下,我们更需要传入子组件 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()
方法。