如何實現(xiàn) Vue 自定義組件中 hover 事件以及 v-model
在CSS中,很容易在鼠標hover時進行更改,只需:
- .item {
- background: blue;
- }
- .item:hover {
- background: green;
- }
在Vue中,它會變得更復(fù)雜一些,因為我們沒有內(nèi)置這個功能。我們必須自己實現(xiàn)這些。不過別擔心,工作量不是很大。
監(jiān)聽正確的事件那么,我們需要監(jiān)聽哪些事件?
我們想知道什么時候鼠標懸停在元素上,這可以通過跟蹤鼠標何時進入元素以及何時離開元素來確定。為了跟蹤鼠標何時離開,可以使用mouseleave事件。
檢測鼠標何時進入可以通過相應(yīng)的mouseenter事件來完成,但是我們不使用這個。
原因是在深度嵌套 DOM 樹上使用mouseenter時可能會出現(xiàn)嚴重的性能問題。這是因為mouseenter向輸入的元素以及每個單獨的祖先元素觸發(fā)一個唯一的事件。
那我們會用什么來代替呢?
我們使用mouseover事件。
二者的本質(zhì)區(qū)別在于,mouseenter不會冒泡,簡單的說,它不會被它本身的子元素的狀態(tài)影響到.但是mouseover就會被它的子元素影響到,在觸發(fā)子元素的時候,mouseover會冒泡觸發(fā)它的父元素.(想要阻止mouseover的冒泡事件就用mouseenter)
為了把知識點串聯(lián)起來,我們使用Vue 事件偵聽鼠標進入和離開時的狀態(tài),并相應(yīng)地更新狀態(tài)。
- <template>
- <div
- @mouseover="hover = true"
- @mouseleave="hover = false"
- />
- </template>
- ----------------------------------
- export default {
- data() {
- return {
- hover: false,
- };
- }
- }
現(xiàn)在通過變量 hover 就可以響應(yīng)鼠標的進入和移出。
在鼠標懸停時顯示一個元素
如果希望顯示基于懸停狀態(tài)的元素,可以將其與v-if指令配對
- <template>
- <div>
- <span
- @mouseover="hover = true"
- @mouseleave="hover = false"
- >
- 鼠標懸停時顯示該內(nèi)容
- </span>
- <span v-if="hover">這里是秘密消息</span>
- </div>
- </template>
- -------------------------------------------------
- export default {
- data() {
- return {
- hover: false,
- };
- }
- }
鼠標懸停時切換樣式類還可以做類似的事情來切換類
- <template>
- <div>
- <span
- @mouseover="hover = true"
- @mouseleave="hover = false"
- :class="{ active: hover }"
- >
- Hover me to change the background!
- </span>
- </div>
- </template>
- -------------------------------------------
- export default {
- data() {
- return {
- hover: false,
- };
- }
- }
- -------------------------------------------
- .active {
- background: green;
- }
雖然這是可行的,但不是最好的解決方案。
對于這種情況,最好使用CSS
- <template>
- <span>
- Hover me to change the background!
- </span>
- </template>
- ----------------------------------------
- span:hover {
- background: green;
- }
將鼠標懸停在一個Vue組件上
如果你想要使用一個Vue組件來實現(xiàn)這種行為,那么我們需要做一些輕微的修改。我們不能像以前那樣聽mouseover和mouseleave事件。
如果Vue組件不發(fā)出那些事件,那么我們就不能監(jiān)聽它們。
相反,我們可以添加.native事件修飾符來直接監(jiān)聽定制Vue組件上的DOM事件。
- <template>
- <my-custom-component
- @mouseover.native="hover = true"
- @mouseleave.native="hover = false"
- />
- </template>
- --------------------------------------
- export default {
- data() {
- return {
- hover: false,
- };
- }
- }
使用.native,我們偵聽本地DOM事件,而不是從Vue組件發(fā)出的事件。
接著我們來看看如何在自定義組件中 實現(xiàn) v-model。
雖然v-model是向普通組件添加雙向數(shù)據(jù)綁定的強大功能,但是如何向自己的自定義組件添加對v-model的支持并不總是那么容易,但其實很簡單。
v-model 介紹
要了解如何在組件中實現(xiàn)v-model支持,需要了解它是如何工作的。雖然看起來很神奇,但v-model= syncedProp實際上是一個非常簡單的簡寫:value= syncedProp @input= syncedProp = arguments[0] (或者 value= syncedProp @input= syncedProp = $event.target)。
因此,要與v-model兼容,你的組件需要做的就是接受:value屬性,并在用戶更改值時發(fā)出@input事件。
基礎(chǔ)事例
假設(shè)有一個日期選擇器組件,該組件在一個對象中接受month和year的值,格式為:{month:1,year:2017}。該組件需要傳入兩個屬性值 month 和 year,,并通過v-model更新綁定對象。
- // DatePicker.vue
- <template>
- <div class="date-picker">
- Month: <input type="number" ref="monthPicker" :value="value.month" @input="updateDate()"/>
- Year: <input type="number" ref="yearPicker" :value="value.year" @input="updateDate()"/>
- </div>
- </template>
- <script>
- export default {
- props: ['value'],
- methods: {
- updateDate() {
- this.$emit('input', {
- month: +this.$refs.monthPicker.value,
- year: +this.$refs.yearPicker.value
- })
- }
- }
- };
- </script>
使用方式:
- // WrapperComponent.vue
- <template>
- <div class="wrapper">
- <date-picker v-model="date"></date-picker>
- <p>
- Month: {{date.month}}
- Year: {{date.year}}
- </p>
- </div>
- </template>
- <script>
- import DatePicker from './DatePicker.vue';
- export default {
- components: {
- DatePicker
- },
- data() {
- return {
- date: {
- month: 1,
- year: 2017
- }
- }
- }
- })
- </script>
如上所看到的,它只是接受一個:value屬性并發(fā)出一個帶有更新日期的@input事件,一點也不復(fù)雜
高級用法
通過使用一個或多個計算屬性,我們可以將輸入數(shù)據(jù)(如字符串)反規(guī)范化為輸入元素更容易處理的格式。這通常與更高級的定制組件一起使用,這些組件必須處理各種可能的輸入格式,比如顏色選擇器。
對于日期選擇器示例,假設(shè)日期傳遞的格式是m/yyyy結(jié)構(gòu)的字符串。通過使用計算屬性(在本例中為splitDate),我們可以將輸入字符串拆分為具有month和year屬性的對象,同時僅對日期選擇器組件進行最少的修改。
- // StringyDatePicker.vue
- <template>
- <div class="date-picker">
- Month: <input type="number" ref="monthPicker" :value="splitDate.month" @input="updateDate()"/>
- Year: <input type="number" ref="yearPicker" :value="splitDate.year" @input="updateDate()"/>
- </div>
- </template>
- <script>
- export default {
- props: ['value'],
- computed: {
- splitDate() {
- const splitValueString = this.value.split('/');
- return {
- month: splitValueString[0],
- year: splitValueString[1]
- }
- }
- },
- methods: {
- updateDate() {
- const monthValue = this.$refs.monthPicker.value;
- const yearValue = this.$refs.yearPicker.value;
- this.$emit('input', `${monthValue}/${yearValue}`);
- }
- }
- };
- </script>
一旦熟悉了這里介紹的概念,可能會發(fā)現(xiàn)自己對任何和每個我們創(chuàng)建的接受用戶輸入的組件都使用了v-model。這是在自己的自定義組件中添加雙向數(shù)據(jù)綁定支持的一種非常簡單但功能強大的方法。