Vue实现组件props双向绑定解决方案

注意: 子组件不能直接修改prop过来的数据,会报错

方案一:

  1. 用data对象中创建一个props属性的副本

  2. watch props属性 赋予data副本 来同步组件外对props的修改

  3. 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)
}
}

效果如下:

 

posted @ 2018-10-09 11:31  进军的蜗牛  阅读(14345)  评论(0编辑  收藏  举报