前言
我有个习惯,就是喜欢逛一些 ui 框架的网站,看看不同的框架都有哪些不一样的组件。发现 vuetifyjs 中有个指令 v-tooltip 好奇他的实现,能不能搬迁到我常用的 element-plus 中使用。于是克隆完源码,剥开实现的指令,核心代码不是很多,但是需要和组件相关联,比如l-tooltip 的值是 content,vuetify 值是 text。不一致,观察到 vuetify 的 Btn 组件为了 Tooltip 是有预留传参接口的。直接使用还是不可行。于是想到 el-tooltip 中有个虚拟用法,传递想要绑定的元素能实现相邻元素的提示效果。最后实现想要的效果。
疑问
从代码书写的出发点来看,嵌套的写法实现 tooltip 是比较的臃肿。
这是一个 button,当 button 过多,代码数量也是很多的。像 element 原生支持的 title="提示"(如下图) 其实很简洁。vue 中指令形式和这个很相似。
下列用 p 元素讲解 title 属性。
<p class="border inline-block cursor-pointer" title="Lorem ipsum">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>
title 书写方式和 v-directive 相似。如果用指令形式完成效果,确实符合原生 html 规范且不失优雅。
在 vuetifyjs 中有我想要的效果
作为前端裁缝,拷贝过来,取其精华,弃之糟粕(贝爷说一切万物掐头去尾都能用),实现目的。
我们在 directives 目录下新建一个文件 名为 useDirectiveComponent.js
import { h, mergeProps, render, resolveComponent } from 'vue'
import { isObject } from '@/utils'
export function useDirectiveComponent(component, props) {
const concreteComponent = typeof component === 'string' ? resolveComponent(component) : component
const hook = mountComponent(concreteComponent, props)
return {
mounted: hook,
updated: hook,
unmounted(el) {
render(null, el)
},
}
}
function mountComponent(component, props) {
return function (el, binding) {
const _props = typeof props === 'function' ? props(el, binding) : props
const text = binding.value?.text ?? binding.value ?? _props?.text
const value = isObject(binding.value) ? binding.value : {}
const children = () => text ?? el.textContent
const node = h(component, mergeProps(_props, value), children)
render(node, el)
}
}
可以看出这是一段通用的指令挂载工具方法。
import { ElTooltip } from 'element-plus'
import { useDirectiveComponent } from '@/directives/useDirectiveComponent'
const Tooltip = useDirectiveComponent(ElTooltip, (el, binding) => {
return {
content: typeof binding.value === 'boolean' ? undefined : binding.value,
placement: binding.arg,
'virtual-triggering': true,
'virtual-ref': el,
}
})
export default (app) => {
app.directive('tooltip', Tooltip)
}
我们注册一下就完成类似的元素原始属性 title 的效果。
效果演示
下面我们看下代码和实际效果
示例代码
<el-row>
<el-col class="mt-40" :span="12" :offset="6">
<!-- v-tooltip -->
<el-button v-tooltip:top="'primary 提示信息'" type="primary">Primary</el-button>
</el-col>
<el-col class="mt-10" :span="12" :offset="6">
<!-- title -->
<p class="border inline-block cursor-pointer" title="Lorem ipsum">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>
</el-col>
<el-col class="mt-10" :offset="6">
<!-- 原始 tooltip 写法 -->
<el-tooltip effect="dark" content="Bottom Left prompts info" placement="bottom-start">
<el-button type="primary">bottom-start</el-button>
</el-tooltip>
</el-col>
</el-row>
我不生产代码,我只是代码的搬运工。