通过v-model控制组件的显示隐藏

之前在用ivew组件库的时候,就一直觉得很好奇,那个组件库的modal是怎么使用v-model来控制显示隐藏的。我印象中v-model是用于view和modal之间的数据双向绑定,官网也有资料:

 

官网对于input,select等标签都有解释,但是却没有div。一般我们在自定义组件的时候,最外层都是套div,控制显示隐藏的时候,就通过父组件传入属性的方式来用v-if去控制。但是iview的组件却没有通过属性,而是直接传递v-model去控制,这就很有意思了。那如果我们自己也想通过v-model去控制div的显示隐藏呢?我没有去看过iview组件的源码,不知道他们是怎么写的,不过有一种办法可以达到那个效果。或许可以尝试一下。

Modal.vue

 1 <template>
 2   <div class="modal-container" v-if="showModal">
 3     <p>我就是一段文字而已。</p>
 4     <div class="bottom-btn">
 5       <button>确定</button>
 6       <button @click="clickCancel">取消</button>
 7     </div>
 8   </div>
 9 </template>
10 
11 <script>
12 export default {
13   props: {
14     value: {
15       default: true,
16       type: Boolean
17     }
18   },
19   watch: {
20     value(val) {
21       this.showModal = val;
22     },
23     showModal(val) {
      // 如果是vue3.0版本,v-model的使用发生了变化,请将下面这句代码替换为this.$emit("update:input",val)
24 this.$emit("input", val); 25 } 26 }, 27 data() { 28 return { 29 showModal: false 30 }; 31 }, 32 methods: { 33 clickCancel() { 34 this.showModal = false; 35 } 36 }, 37 mounted() { 38 } 39 }; 40 </script> 41 42 <style scoped> 43 .modal-container { 44 background: #eee; 45 width: 300px; 46 height: 240px; 47 position: absolute; 48 top: 0; 49 left: 0; 50 right: 0; 51 bottom: 0; 52 margin: auto; 53 border-radius: 10px; 54 } 55 .bottom-btn { 56 display: flex; 57 align-items: flex-end; 58 height: 70%; 59 justify-content: center; 60 } 61 .bottom-btn button { 62 outline: none; 63 border: none; 64 width: 70px; 65 height: 40px; 66 line-height: 40px; 67 text-align: center; 68 } 69 </style>

先建了一个Modal的组件,定义一个变量showModal用于控制该组件是否显示,这个组件内部接收一个value的属性,它是一个布尔值,主要就是通过这个外部的值来判断显示隐藏,但是vue不建议直接修改父组件传进来的数据,因此把value的值赋值给showModal。很重要的一步就是侦听器这里,监听了value值也监听了组件内部的showModal,外部传入value时,值改变会触发这个侦听器,然后在这里把值给showModal,showModal又绑定到了v-if上。而在点击取消按钮时,showModal发生改变会被监听到,所以发送了input事件,但是这个input事件我感觉并没有发送给父组件,而是直接触发的div自身的input事件,一般通过$emit去发送事件时,父组件是需要去实现这个事件的,而在这个例子很明显父组件并没有去实现这个input,但是父组件的值却被修改了,那它是怎么做到的呢。其实看到这里在结合官网的解释或许基本上就能明白了:“v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理”。

因为div本身就有input事件,只不过用的比较少。把div标签的contenteditable属性设置为true就可以把div变成输入框,既然如此那它当然就具备输入框的功能和事件了。所以这里之所以不需要父组件去实现子组件发送的事件,感觉上是因为发送的这个事件直接触发了它本身的input,触发之后vue自动的把input事件所传递的值赋值给了v-model所绑定的值,然后Modal组件监听到value改变为了false,赋值给了showModal。看起来似乎有点绕,但感觉大致上是这样。

HelloWorld.vue:

<template>
  <div>
    <button @click="clickOpen">打开弹出框</button>
    <Modal v-model="openModal"></Modal>
  </div>
</template>

<script>
import Modal from "./Modal";
export default {
  name: "HelloWorld",
  components:{
    Modal
  },
  data() {
    return {
      openModal:false
    };
  },
  methods: {
    clickOpen(){
      this.openModal = true
    }
  }
};
</script>

这样就可以不用属性的方式,而是通过v-model的方式去控制div的隐藏和显示了。其实就算不用v-model也可以直接实现,那就是通过属性也一样,让子组件同样的通过该属性绑定v-if,因为Modal内部本质上也是在使用v-if控制。但如果通过属性的方式去做,那这个子组件就需要让父组件去修改是否显示的布尔值,因为通过属性传进来的值,只能由父组件修改,否则报错。所以对比一下,这个方式还是很有作用的。

(在vue3.0当中,v-model的使用发生了一些变化,因此这里的父组件也相应的改变,以上写法是vue2.0版本,如果vue为3.0,请将modal组件上面的v-model="openModal"替换为v-model:value="openModal")

 

posted @ 2020-04-18 12:52  _花落  阅读(4805)  评论(0编辑  收藏  举报