之前在用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")