Vue实现组件props双向绑定解决方案
注意: 子组件不能直接修改prop过来的数据,会报错
方案一:
-
用data对象中创建一个props属性的副本
-
watch props属性 赋予data副本 来同步组件外对props的修改
-
watch data副本,emit一个函数 通知到组件外
HelloWorld组件代码如下:(代码里面有相应的注释)
<template> <div class="hello"> <h1 v-show="visible">测试显示隐藏</h1> <div @click="cancel">点我点我</div> </div> </template> <script> export default { name: 'HelloWorld', props: { value: { type: Boolean, default:false } }, data () { return { visible: false } }, watch:{ value(val) { this.visible = val; },
// 只有这一步 才触发父组件的方法 由父组件的 paretnVisibleChange 方法去改变父组件的数据
visible(val) { this.$emit("paretnVisibleChange",val); } },
// 子组件修改的只能是子组件 本身的data数据 methods:{ cancel(){ this.visible = !this.visible; } },
// 注意这段代码 为了同步父组件的数据 mounted() { if (this.value) { this.visible = true; } } } </script> <style scoped> </style>
父组件代码如下:
<template> <div id="app"> <HelloWorld :value = 'visible' @paretnVisibleChange="visibleChange" /> </div> </template> <script> import HelloWorld from './components/HelloWorld' export default { name: 'App', components: { HelloWorld }, data () { return { visible: true } }, methods:{
// 父子组件就是靠的这个方法改变数据的 visibleChange(val){ this.visible = val; } } } </script>
方案一 的缺点就是 父组件必须有个 visibleChange 这样的方法,有点累赘。
这时候 想到了 v-model
因为
<input v-model = 'someThing'>
是下面这段代码的语法糖
<input :value = 'someThing' @input = 'someThing = $event.target.value'>
也就是说 v-mode 自带了 一个改变父组件的方法 类似方案一的 paretnVisibleChange
但是使用 v-model 的时候 需要注意两点:
1. 子组件要接受 value 属性
2. value改变时 要触发input 事件
方案二:
HelloWorld 子组件的代码如下;
<template> <div class="hello"> <h1 v-show="visible">测试显示隐藏</h1> <div @click="cancel">点我点我</div> </div> </template> <script> export default { name: 'HelloWorld', props: { value: { type: Boolean, default:true } }, data () { return { visible: false } }, watch:{ value(val) { this.visible = val; },
// 子组件 改变的就是这段代码 visible(val) { this.$emit("input",val); } }, methods:{ cancel(){ this.visible = !this.visible; } }, mounted() { if (this.value) { this.visible = true; } } } </script>
父组件代码如下:(父组件省去了 paretnVisibleChange 方法)
<template> <div id="app"> <HelloWorld v-mode = 'visible'/> </div> </template> <script> import HelloWorld from './components/HelloWorld' export default { name: 'App', components: { HelloWorld }, data () { return { visible: true } } } </script>
方案三:
vue 2.3.0之后新增了 .sync 属性 使用方法跟 v-model 类似 具体 请参考 : https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-修饰符
下面我写了一个简单的sync 的使用实例:
父组件的代码如下:
<li is="DragCompent" v-for="(item, index) in layoutItem" :item="item" v-model="cloneLeftItemText" :leftDragItemIsDraged.sync = 'leftDragItemIsDraged' :key="index"></li>
子组件的代码如下:
props: { leftDragItemIsDraged: { type: Boolean, default: false } }, watch:{ leftDragItemIsDraged(val) { this.thisLeftDragItemIsDraged = val; }, thisLeftDragItemIsDraged(val){ this.$emit('update:leftDragItemIsDraged', val) } }
效果如下: