Vue2 ,v-model,以及利用 v-model 实现组件props双向绑定的解决方案

一、v-model?

https://segmentfault.com/a/1190000009492595

v-model 其实是一个语法糖,这背后其实做了两个操作

  1. v-bind 绑定一个 value 属性

  2. v-on 指令给当前元素绑定 input 事件

在原生表单元素中

<input v-model='something'>

就相当于

<input v-bind:value="something" v-on:input="something = $event.target.value">

在自定义组件中

<my-component v-model='something'></my-componment>

相当于

<my-component v-bind:value='something' v-on:input='something = arguments[0]'></my-component>

这时候,something接受的值就是input是事件的回掉函数的第一个参数
所以在自定义的组件当中,要实现数据绑定,还需要使用[$emit]去触发input的事件。

this.$emit('input', value)

二、利用 v-model 实现组件props双向绑定的解决方案?

https://segmentfault.com/a/1190000008662112

https://cn.vuejs.org/v2/guide/components.html#%E4%BD%BF%E7%94%A8%E4%BA%8B%E4%BB%B6%E6%8A%9B%E5%87%BA%E4%B8%80%E4%B8%AA%E5%80%BC

在项目中使用vue2来构建项目,跟 vue1 很大的一处不同在于2 取消了props 的双向绑定,改成只能从父级传到子级的单向数据流,初衷当然是好的,为了避免双向绑定在项目中容易造成的数据混乱。

解决方案一、

参考网上与github上,解决方案都是这样:

1、在父组件中,用data对象中创建一个props属性的副本

2、在子组件中,watch props属性,并赋予data副本,同步组件外对props的修改

3、在子组件中,watch data副本,emit一个方法,通知到组件外。

最常见的 modal为例子:

modal挺合适双向绑定的,外部可以控制组件的 显示或者隐藏,组件内部的关闭可以控制 visible属性隐藏,同时visible 属性同步传输到外部。

///modal.vue  组件
<template>
  <div class="modal" v-show="visible">
      <div class="close" @click="cancel">X</div>
  </div>
</template>

<script>
export default {
    name:'modal',
    props: {
      value: {
        type: Boolean,
        default:false
      }
    },

  data () {
    return {
      visible:false
    }
  },
  watch:{
      value(val) {
        this.visible = val;
      },
      visible(val) {
        this.$emit("visible-change",val);
      }
  },
  methods:{
    cancel(){
      this.visible = false;
    }
  },
  mounted() {
    if (this.value) {
      this.visible = true;
    }
  }
}
</script>


///调用modal组件
<modal :value="isShow" @visible-change="modalVisibleChange"></modal>

export default {
  name: 'app',
  data () {
    return {
      isShow:true,
    }
  },
  methods:{
     modalVisibleChange(val){
       this.isShow = val;
     }
  }
}

这样就解决了 组件props 双向绑定的问题。 但是这样有一个不是太美观的现象就是 在父级调用 modal组件的时候,还需要写一个 modalVisibleChange 的methods. 总是显得这部分代码是多余的。 特别是写一个让别人用的公共组件,这样调用太麻烦了。

能不能不写method来实现props的双向绑定呢,答案是可以的。

优美解决方案、

那就是利用 v-model, 然后使用value来保存v-model的值,进行双向绑定

<template>
  <div class="modal" :value="value" v-show="visible">
      <div class="close" @click="cancel">X</div>
  </div>
</template>

<script>
export default {
    props: {
      value: {
        type: Boolean,
        default:false
      }
    },

  data () {
    return {
      visible:false
    }
  },
  watch:{
      value(val) {
        this.visible = val;
      },
      visible(val) {
        this.$emit('input', val);
      }
  },
  methods:{
    cancel(){
      this.visible = false;
    }
  },
  mounted() {
    if (this.value) {
      this.visible = true;
    }
  }
}
</script>


///调用modal组件

  <modal v-model="isShow"></modal>

export default {
  name: 'app',
  data () {
    return {
      isShow:false
    }
  }
}
</script>

只要设置 isShow 就可以控制 modal 组件的显示或者隐藏,同时 如果是modal 组件内部关闭按钮关闭的,状态也会传到 isShow。

实例:实现公共组件

<template>
	<transition name="fade">
		<div v-show="visible">
			<div v-if="showModal"></div>
			<div @click="handleWrapClick">
				<div>
					<div>
						<slot name="tips"></slot>
						<div @click="close" v-if="showClose">
							<img src="../../assets/images/intent/close.png">
						</div>
					</div>
					<slot name="footer"></slot>
				</div>
			</div>
		</div>
	</transition>
</template>
<script>
	export default {
	    name: 'confirm',
	    props: {
		    showModal: {
		        type: Boolean,
		        default: false
		    },
		    showClose: {
		        type: Boolean,
		        default: false
	        },
            value: {
                type: Boolean,
                default: false
            },
            closable: {
                type: Boolean,
                default: true
            },
            maskClosable: {
                type: Boolean,
                default: false
		    },
		    authClose: {
		        type: Boolean,
		        default: true
		    },
		    model: {
		        type: String,
		        default: ''
		    }
	    },
		data(){
			return {
				visible: this.value,
				timer: null
			}
		},
		watch: {
			value(val){
                 this.visible = val
			},
			visible(val){
				if(val === false){
					if(this.timer){
						clearTimeout(this.timer)
					}
					return
				}
				if(this.authClose){
					this.timer = setTimeout(() => {
						this.close()
					}, 2500)
				}
			}
		},
		methods: {
			close(){
				this.visible = false
				if(this.model){
					this.$parent[this.model] = false
				}
                        },
                        mask(){
                            if(this.maskClosable){
                                    this.close()
                            }
			},
			handleWrapClick(event){
				if(event.target == event.currentTarget){
					this.mask()
				}
            }
		}
	}
<confirm v-model="showConfirm" model="showConfirm" maskClosable showModal :showClose="showClose" :authClose="authClose">
			<div slot="tips">
				<div :class="confirmImgClass"></div>
				<div v-html="confirmTips"></div>
			</div>
			<div slot="footer" v-if="confirmBtnList && confirmBtnList.length">
				<div v-for="btn in confirmBtnList" :key="btn.btnText" @click="btn.btnCallBack" :class="{active: btn.active}">{{btn.btnText}}</div>
			</div>
		</confirm>

        data () {
		return {
			flag: true,
			showOptTips: false,
			optTips: '',
			showConfirm: false,
			showClose: false,
			authClose: false,
			confirmTips: '备注仅能添加一次,请确定已经完善内容。',
			confirmImgClass: 'confirm-yellow-warning',
			confirmBtnList: []
		}
	},

        submit() {
			this.confirmBtnList = [
				{
					btnText: '提交',
					btnClass: '',
					active: false,
					btnCallBack: () => {
						this.showConfirm = false;
						this.isSubmit (true)
					}
				},
				{
					btnText: '取消',
					btnClass: '',
					btnCallBack: () => {
						this.showConfirm = false;
						this.isSubmit (false)
					}
				}
			]

			if (this.flag) {
				this.showConfirm = true;
			} else {
				this.showConfirm = false;
			}
		},
        isSubmit (msg) {				
			if (msg) {
				this.flag = false;
                  //处理部分
            } else {
				this.flag = true;
                //处理部分
        }

猜你喜欢

转载自blog.csdn.net/github_39598787/article/details/80420642
今日推荐