如何实现 Vue 自定义组件中 hover 事件以及 v-model

在CSS中,很容易在鼠标hover时进行更改,只需:

1.item {
2  background: blue;
3}
4
5.item:hover {
6  background: green;
7}

在Vue中,它会变得更复杂一些,因为我们没有内置这个功能。我们必须自己实现这些。不过别担心,工作量不是很大。

监听正确的事件

那么,我们需要监听哪些事件?

我们想知道什么时候鼠标悬停在元素上,这可以通过跟踪鼠标何时进入元素以及何时离开元素来确定。为了跟踪鼠标何时离开,可以使用mouseleave事件。

检测鼠标何时进入可以通过相应的mouseenter事件来完成,但是我们不使用这个。

原因是在深度嵌套 DOM 树上使用mouseenter时可能会出现严重的性能问题。这是因为mouseenter向输入的元素以及每个单独的祖先元素触发一个唯一的事件。

那我们会用什么来代替呢?

我们使用mouseover事件。

二者的本质区别在于,mouseenter不会冒泡,简单的说,它不会被它本身的子元素的状态影响到.但是mouseover就会被它的子元素影响到,在触发子元素的时候,mouseover会冒泡触发它的父元素.(想要阻止mouseover的冒泡事件就用mouseenter)

为了把知识点串联起来,我们使用Vue 事件侦听鼠标进入和离开时的状态,并相应地更新状态。

 1<template>
 2  <div
 3    @mouseover="hover = true"
 4    @mouseleave="hover = false"
 5  />
 6</template>
 7----------------------------------
 8
 9export default {
10  data() {
11    return {
12      hover: false,
13    };
14  }
15}

现在通过变量 hover 就可以响应鼠标的进入和移出。

在鼠标悬停时显示一个元素

如果希望显示基于悬停状态的元素,可以将其与v-if指令配对

 1<template>
 2  <div>
 3    <span
 4      @mouseover="hover = true"
 5      @mouseleave="hover = false"
 6    >
 7      鼠标悬停时显示该内容
 8    </span>
 9    <span v-if="hover">这里是秘密消息</span>
10  </div>
11</template>
12
13-------------------------------------------------
14
15export default {
16  data() {
17    return {
18      hover: false,
19    };
20  }
21}

鼠标悬停时切换样式类

还可以做类似的事情来切换类

 1<template>
 2  <div>
 3    <span
 4      @mouseover="hover = true"
 5      @mouseleave="hover = false"
 6      :class="{ active: hover }"
 7    >
 8      Hover me to change the background!
 9    </span>
10  </div>
11</template>
12-------------------------------------------
13
14export default {
15  data() {
16    return {
17      hover: false,
18    };
19  }
20}
21-------------------------------------------
22
23.active {
24  background: green;
25}

虽然这是可行的,但不是最好的解决方案。

对于这种情况,最好使用CSS

 1<template>
 2  <span>
 3    Hover me to change the background!
 4  </span>
 5</template>
 6
 7----------------------------------------
 8
 9span:hover {
10  background: green;
11}

将鼠标悬停在一个Vue组件上

如果你想要使用一个Vue组件来实现这种行为,那么我们需要做一些轻微的修改。我们不能像以前那样听mouseovermouseleave事件。

如果Vue组件不发出那些事件,那么我们就不能监听它们。

相反,我们可以添加.native事件修饰符来直接监听定制Vue组件上的DOM事件。

 1<template>
 2  <my-custom-component
 3    @mouseover.native="hover = true"
 4    @mouseleave.native="hover = false"
 5  />
 6</template>
 7--------------------------------------
 8export default {
 9  data() {
10    return {
11      hover: false,
12    };
13  }
14}

使用.native,我们侦听本地DOM事件,而不是从Vue组件发出的事件。

接着我们来看看如何在自定义组件中 实现 v-model

虽然v-model是向普通组件添加双向数据绑定的强大功能,但是如何向自己的自定义组件添加对v-model的支持并不总是那么容易,但其实很简单。

v-model  介绍

要了解如何在组件中实现v-model支持,需要了解它是如何工作的。虽然看起来很神奇,但v-model= syncedProp实际上是一个非常简单的简写:value= syncedProp @input= syncedProp = arguments[0] (或者 value= syncedProp @input= syncedProp = $event.target)。

因此,要与v-model兼容,你的组件需要做的就是接受:value属性,并在用户更改值时发出@input事件。

基础事例

假设有一个日期选择器组件,该组件在一个对象中接受monthyear的值,格式为:{month:1,year:2017}。该组件需要传入两个属性值 monthyear,,并通过v-model更新绑定对象。

 1// DatePicker.vue
 2<template>
 3  <div class="date-picker">
 4    Month: <input type="number" ref="monthPicker" :value="value.month" @input="updateDate()"/>
 5    Year: <input type="number" ref="yearPicker" :value="value.year" @input="updateDate()"/>
 6  </div>
 7</template>
 8
 9<script>
10export default {
11  props: ['value'],
12
13  methods: {
14    updateDate() {
15      this.$emit('input', {
16        month: +this.$refs.monthPicker.value,
17        year: +this.$refs.yearPicker.value
18      })
19    }
20  }
21};
22</script>
23

使用方式:

 1// WrapperComponent.vue
 2
 3<template>
 4  <div class="wrapper">
 5    <date-picker v-model="date"></date-picker>
 6    <p>
 7      Month: {{date.month}}
 8      Year: {{date.year}}
 9    </p>
10  </div>
11</template>
12
13<script>
14import DatePicker from './DatePicker.vue';
15
16export default {
17  components: {
18    DatePicker
19  },
20
21  data() {
22    return {
23      date: {
24        month: 1,
25        year: 2017
26      }
27    }
28  }
29})
30</script>

如上所看到的,它只是接受一个:value属性并发出一个带有更新日期的@input事件,一点也不复杂

高级用法

通过使用一个或多个计算属性,我们可以将输入数据(如字符串)反规范化为输入元素更容易处理的格式。这通常与更高级的定制组件一起使用,这些组件必须处理各种可能的输入格式,比如颜色选择器。

对于日期选择器示例,假设日期传递的格式是m/yyyy结构的字符串。通过使用计算属性(在本例中为splitDate),我们可以将输入字符串拆分为具有monthyear属性的对象,同时仅对日期选择器组件进行最少的修改。

 1// StringyDatePicker.vue
 2<template>
 3  <div class="date-picker">
 4    Month: <input type="number" ref="monthPicker" :value="splitDate.month" @input="updateDate()"/>
 5    Year: <input type="number" ref="yearPicker" :value="splitDate.year" @input="updateDate()"/>
 6  </div>
 7</template>
 8
 9<script>
10export default {
11  props: ['value'],
12
13  computed: {
14    splitDate() {
15      const splitValueString = this.value.split('/');
16
17       return {
18        month: splitValueString[0],
19        year: splitValueString[1]
20      }
21    }
22  },
23
24  methods: {
25    updateDate() {
26      const monthValue = this.$refs.monthPicker.value;
27      const yearValue = this.$refs.yearPicker.value;
28      this.$emit('input', `${monthValue}/${yearValue}`);
29    }
30  }
31};
32</script>
33

一旦熟悉了这里介绍的概念,可能会发现自己对任何和每个我们创建的接受用户输入的组件都使用了v-model。这是在自己的自定义组件中添加双向数据绑定支持的一种非常简单但功能强大的方法。


作者:Joshua Bemenderfe  译者:前端小  来源:alligator

原文:

https://alligator.io/vuejs/add-v-model-support/
https://michaelnthiessen.com/hover-in-vue/


交流

这些优化技巧可以避免我们在JS中过多的使用IF语句

适合前端开和UI设计的20多个最佳ICON库

27个CSS面试的高频考点助力金三银四

纯CSS特效,5分钟带你学会制作有个性的滚动条

将多个属性传递给&nbsp;Vue&nbsp;组件的几种方式

  • 13
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
你可以使用以下步骤来自定义Vue下拉框的样式: 1.创建一个Vue组件来显示下拉框。可以使用v-model指令来绑定选的值。 2.使用CSS样式来自定义下拉框的外观。可以使用伪元素和CSS属性来实现下拉框的箭头和选项列表的样式。 3.为下拉框组件添加事件监听器,以便在用户选择选项时更新选的值。 下面是一个简单的Vue下拉框组件示例,你可以参考它来进行自定义样式: ``` <template> <div class="custom-select" :class="{ open: isOpen }"> <div class="selected" @click="toggleDropdown">{{ selected }}</div> <ul class="options" v-show="isOpen"> <li v-for="option in options" :key="option.value" @click="selectOption(option)">{{ option.label }}</li> </ul> </div> </template> <script> export default { props: { options: { type: Array, required: true }, value: { type: String, required: true } }, data() { return { isOpen: false, selected: '' } }, computed: { selectedOption() { return this.options.find(option => option.value === this.value) } }, mounted() { this.selected = this.selectedOption.label }, methods: { toggleDropdown() { this.isOpen = !this.isOpen }, selectOption(option) { this.selected = option.label this.$emit('input', option.value) this.isOpen = false } } } </script> <style> .custom-select { position: relative; width: 200px; } .selected { padding: 10px; background-color: #fff; border: 1px solid #ccc; cursor: pointer; } .selected::after { content: ''; position: absolute; top: 50%; right: 10px; transform: translateY(-50%); border: 6px solid transparent; border-top-color: #000; } .options { position: absolute; top: 100%; left: 0; width: 100%; max-height: 200px; overflow-y: auto; background-color: #fff; border: 1px solid #ccc; z-index: 1; } .options li { padding: 10px; cursor: pointer; } .options li:hover { background-color: #f2f2f2; } .open .selected::after { border-top-color: #fff; } </style> ``` 在上面的示例,我们使用CSS样式来创建自定义下拉框的外观。箭头使用伪元素和CSS属性来实现,选项列表使用CSS属性来设置最大高度和滚动。 在Vue组件,我们使用v-for指令来循环渲染选项列表,并使用v-show指令来控制选项列表的显示和隐藏。在事件处理程序,我们使用$emit方法来触发input事件,以便父组件可以获取选的值。 希望这能帮助到你!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@大迁世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值