需求分析
Tooltip
组件要实现的需求就是当我们点击或者鼠标移入的时候显示对应的文字提示。同时文字的内容、位置以及显示、隐藏的延迟时间还有是否使用过渡效果等。这里要注意的是我们使用popperjs
来实现文字位置的展示。它的原理也很简单:通过计算和定位,将弹出元素放在与目标元素相关的位置上。另外popperjs
还会监听窗口的变化,以便在窗口大小或滚动位置发生变化时,重新计算和定位弹出元素。那么我们先看一下Tooltip
组件有哪些配置项:
1 | // types.ts |
TooltipProps
定义了我们这个Tooltip
组件可以接受哪些属性; TooltipEmits
是我们定义的Tooltip
组件的事件类型接口,有两个事件,一个是点击了元素以外区域,另一个是当显示、隐藏发生变化的时候;TooltipInstance
是我们向外暴露一个实例,它的类型接口有两个方法分别是show
和hide
用来控制显示和隐藏。
组件基本结构
Tooltip
组件的结构还是非常简单的,根据需求分析阶段,我们知道它有一个内容,我们使用 vue 中的<slot></slot>
插槽。还有一个过渡效果,那么我们就用 vue 中的transition
来做。基本结构如下:
1 | <template> |
组件事件分析
Tooltip
组件有哪些行为(事件),最容易想到的就是当我们鼠标移入的时候显示,移出的时候隐藏;还有一个就是点击元素的时候显示,再次点击元素隐藏。一句话里面包含了三个事件,分别是鼠标移入mouseover
、鼠标移出mouseout
、鼠标点击click
。根据这一点儿我们可以写出以下代码:
1 | import { reactive } from 'vue' |
绑定好事件以后,我们来写具体的事件回调函数。我们应该声明一个响应式变量来控制元素的隐藏和显示,当发生改变的时候通过emits
将变量的值暴露出去,可以让外部组件也就是父组件在有需要的时候访问到。在这里我们还应该考虑一个频率的问题,所以使用lodash-es
封装好的工具函数debounce
来做防抖效果。
1 | npm install lodash-es --save |
1 | import { ref } from 'vue' |
上面三个事件的回调函数写完以后我们还要考虑一个问题就是,当我们点击了元素外面的区域以后,也应该将Tooltip
组件的内容隐藏起来。判断是否点击了元素外部区域不光在这一个组件中可能用到,其他组件也有可能使用。所以我们写一个组合式函数
,后面其他组件有需要的时候可以直接使用。在vue2
当中的时候是用mixin
混入来实现的,vue3
提供了组合式函数
这个概念。下面我们来编写实现一下这个组合式函数:
1 | // 按照惯例 组合式函数以“use”开头 |
使用组合式函数useClickOutside
当我们编写好组合式函数useClickOutside
以后,我们就要在Tooltip
组件中使用它:
1 | import { TooltipEmits } from './types' |
创建 popperjs 实例
在实现了一系列事件以后,我们该说到如何将文字提示准确的定位在触发元素的某一位置上。前面我们分析过了,使用的是popperjs
做到的。还应该考虑一个问题就是显示和隐藏的时机,前面我们声明了一个响应式变量isOpen
,那么我们就用侦听器来监听它的变化,从而做出对应的变化。接下来就让我们编写代码实现它:
安装 popperjs
1 | npm install @popperjs/core --save |
1 | import { watch, onUnmounted } from 'vue' |
在上面创建popperjs
实例的时候有用到了一个叫popperOptions
的响应式对象,这个响应式对象是我们通过计算属性返回的,用来做一些默认的配置项:
1 | import { computed } from 'vue' |
监听触发方式的变化
我们的Tooltip
组件支持两种触发方式,所以我们要监听外部传进来的trigger
,当它发生变化的时候我们应该清空之前的响应式事件对象,重新绑定事件。
1 | import { watch } from 'vue' |
将组件方法导出去
我们这个Tooltip
组件有可能会在其他组件中使用,所以将控制显示、隐藏的方法暴露出去,供外部使用:
1 | import { TooltipInstance } from './types' |
知识点
在写的过程中,有一些知识点不清楚,记录下来。
Record<Keys, Type>
构造一个对象类型,其属性键为Keys
, 其属性值为Type
。此工具类型可用于将一种类型的属性映射到另一种类型。Partial<Type>
构造一个将Type
的所有属性设置为可选的类型。withDefaults
Vue
中提供的编译器宏,为了解决defineProps
声明的时候没有可以给props
提供默认值的方式。defineExpose
Vue
中使用<script setup>
的组件默认是关闭的————通过模板的引用或者$parent
链获取到的组件的公开实例,不会暴露任何在<script setup>
中声明的绑定。可以通过defineExpose
编译器宏来显示指定在<script setup>
组件中要暴露出去的属性。