之前在用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) { 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: { inputContent(val){ console.log(val) }, clickOpen(){ this.openModal = true } } }; </script>
这样就可以不用属性的方式,而是通过v-model的方式去控制div的隐藏和显示了。其实就算不用v-model也可以直接实现,那就是通过属性也一样,让子组件同样的通过该属性绑定v-if,因为Modal内部本质上也是在使用v-if控制。其实也没有什么区别。我个人仅仅只是好奇iview的实现方式而已。当然了,毕竟没看过iview源码,他们是不是这么做的不太清楚,但这只是一种方式而已。不用太在意。多了解一点总没坏处。