1、首先是一个简单的示例
三个文件
app.vue # 自定义的业务,调用自己封装的组件child.vue child.vue # 自己封装的第三方组件plug.vue,便于上层调用 plug.vue # 第三方组件,提供v-model数据绑定
调用层次结构
app -> child -> plug
child在这里起到一个承上启下的桥梁功能
代码
app.vue
<template> <div> <p >app.vue 父组件:{{value}}</p> <button @click="changeValue">父组件+1</button> <child v-model="value"/> </div> </template> <script> import child from './Child.vue'; export default { components:{ child }, data(){ return { value: 0 } }, methods:{ changeValue(){ this.value++; } } } </script>
Child.vue
<template> <div> <p>child.vue 子组件:{{value}}</p> <button @click="changeValue">子组件+1</button> <plug v-model="value" @input="$emit('input', $event)"/> </div> </template> <script> import plug from "./Plug.vue"; export default { props: ["value"], components: { plug }, methods: { changeValue() { this.value++; this.$emit('input', this.value) } } }; </script>
Plug.vue
<template> <div> <p>plug.vue 插件:{{value}}</p> <input type="text" :value="value" @input="$emit('input', $event.target.value)" /> </div> </template> <script> export default { props: ["value"] }; </script>
修改app值
这样做可以实现 app<->child<->plug 之间数据同步,不过会有一个警告
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
原因是vue2中的数据只能单向流动,不能修改外层数据
child 组件中修改了value值
解决方案
修改child.vue 的value为data属性, 警告消失
<template> <div> <p>child.vue 子组件:{{value}}</p> <button @click="changeValue">子组件+1</button> <plug v-model="innerVlaue" @input="$emit('input', $event)"/> </div> </template> <script> import plug from "./Plug.vue"; export default { props: ["value"], components: { plug }, // 添加data属性 data(){ return { innerVlaue: this.value } }, methods: { changeValue() { this.innerVlaue++; this.$emit('input', this.innerVlaue) } } }; </script>
遇到如下问题
1、修改app的值,可以传递到child,不能传递到plug
2、修改child的值,可以传递到child 和 app
3、修改plug插件的值,可以传递到child 和 app
继续解决问题
修改child的 data 属性为computed 属性
此时app, child, plug数据都可以正常传递
<template> <div> <p>child.vue 子组件:{{innerVlaue}}</p> <button @click="changeValue">子组件+1</button> <plug v-model="innerVlaue" /> </div> </template> <script> import plug from "./Plug.vue"; export default { props: ["value"], computed: { innerVlaue:{ get(){ return this.value }, set(newValue){ this.$emit("input", newValue) } } }, components: { plug }, methods: { changeValue() { this.innerVlaue++; } } }; </script>
至此,子孙三代的数据传递正常,warn问题解决
自始至终,app, plug 的代码都没有做修改,只是修改了child 中的data属性或computed
所以,要正常通信,需要修改中间数据层的传递方式,既要考虑父级组件数据流入,也要兼顾子组件的事件传入
最后在child中使用 computed 计算属性完成了承上启下数据传递
v-model同样可以使用.sync实现,只是写法不一样
如果子组件不需要获取父组件数据,父组件直接可以和插件通信, 可以参看文章: