cursor-关于自定义指令的问题处理
问题描述:
今天碰见一个关于自定义指令的问题,在项目中关于el-tooltip组件封装了一个自定义组件,功能就是常规的悬浮窗显示鼠标选中的文本,做一个具体的全部展示。问题是这个数据是会更新的,但是悬浮窗中的文本数据仍然是前一次的数据,所以鼠标悬浮之后显示为上一次的数据,造成了这个问题,当然只要是定位到了问题,并且可以复现,那么还是老规矩,先交给cursor去试试。
修改前:
import type { DirectiveBinding } from 'vue'
import { ElTooltip } from 'element-plus'
import { h, render } from 'vue'function setupTruncateTooltip(el: HTMLElement, binding: DirectiveBinding) {const updateTooltip = () => {const text = el.innerTextconst classes = el.classNameconst inputWidth = el.offsetWidthconst scrollWidth = el.scrollWidth// 内容长度大于元素长度时,显示tooltipif (scrollWidth > inputWidth) {if (!el.parentElement?.classList.contains('el-tooltip__trigger')) {// 创建tooltip vnodeconst tooltipVNode = h(ElTooltip, {content: text,placement: 'top-end',effect: 'dark',}, () => [h('span', { class: classes }, { default: () => el.cloneNode(true) })])// 渲染tooltipconst container = document.createElement('div')render(tooltipVNode, container)// 找到tooltip元素const tooltipElement = container.querySelector('.el-tooltip__trigger') || container.firstElementChildif (tooltipElement) {el.parentNode?.insertBefore(tooltipElement, el)// el 增加一个style 样式为宽度100%el.style.width = '100%'tooltipElement.appendChild(el)}}} else {const tooltipTrigger = el.closest('.el-tooltip__trigger')// 清除style为100%el.style.cssText = ''if (tooltipTrigger && tooltipTrigger !== el) {tooltipTrigger.parentNode?.insertBefore(el, tooltipTrigger)tooltipTrigger.parentNode?.removeChild(tooltipTrigger)}}}// 初始设置updateTooltip()// 监听元素内容变化const observer = new MutationObserver(updateTooltip)observer.observe(el, {childList: true,characterData: true,subtree: true,})// 监听窗口大小变化window.addEventListener('resize', updateTooltip)// 清理函数el._cleanup = () => {observer.disconnect()window.removeEventListener('resize', updateTooltip)}
}export default {mounted(el: HTMLElement, binding: DirectiveBinding) {setupTruncateTooltip(el, binding)},updated(el: HTMLElement, binding: DirectiveBinding) {setupTruncateTooltip(el, binding)},
}
修改后:
import type { DirectiveBinding } from 'vue'
import { ElTooltip } from 'element-plus'
import { h, render, nextTick } from 'vue'// 扩展 HTMLElement 类型,添加自定义属性
declare global {interface HTMLElement {_tooltipInstance?: any_cleanup?: () => void_tooltipContainer?: HTMLElement}
}function setupTruncateTooltip(el: HTMLElement, binding: DirectiveBinding) {const updateTooltip = async () => {// 等待 DOM 更新完成await nextTick()const text = el.innerTextconst classes = el.classNameconst inputWidth = el.offsetWidthconst scrollWidth = el.scrollWidth// 内容长度大于元素长度时,显示tooltipif (scrollWidth > inputWidth) {if (!el.parentElement?.classList.contains('el-tooltip__trigger')) {// 创建tooltip vnodeconst tooltipVNode = h(ElTooltip, {content: text,placement: 'top-end',effect: 'dark',}, () => [h('span', { class: classes }, { default: () => el.cloneNode(true) })])// 渲染tooltipconst container = document.createElement('div')render(tooltipVNode, container)// 找到tooltip元素const tooltipElement = container.querySelector('.el-tooltip__trigger') || container.firstElementChildif (tooltipElement) {el.parentNode?.insertBefore(tooltipElement, el)// el 增加一个style 样式为宽度100%el.style.width = '100%'tooltipElement.appendChild(el)// 保存 tooltip 实例和容器引用,用于后续更新el._tooltipInstance = tooltipVNodeel._tooltipContainer = container}} else {// 如果已经存在 tooltip,更新其内容updateExistingTooltip(el, text)}} else {const tooltipTrigger = el.closest('.el-tooltip__trigger')// 清除style为100%el.style.cssText = ''if (tooltipTrigger && tooltipTrigger !== el) {tooltipTrigger.parentNode?.insertBefore(el, tooltipTrigger)tooltipTrigger.parentNode?.removeChild(tooltipTrigger)// 清理引用el._tooltipInstance = undefinedel._tooltipContainer = undefined}}}// 更新已存在的 tooltip 内容const updateExistingTooltip = (el: HTMLElement, newText: string) => {if (el._tooltipInstance && el._tooltipContainer) {// 重新创建 tooltip vnode 并更新内容const newTooltipVNode = h(ElTooltip, {content: newText,placement: 'top-end',effect: 'dark',}, () => [h('span', { class: el.className }, { default: () => el.cloneNode(true) })])// 重新渲染render(newTooltipVNode, el._tooltipContainer)// 更新引用el._tooltipInstance = newTooltipVNode}}// 初始设置updateTooltip()// 监听元素内容变化const observer = new MutationObserver(() => {// 使用防抖,避免频繁更新clearTimeout(el._updateTimer)el._updateTimer = setTimeout(updateTooltip, 50)})observer.observe(el, {childList: true,characterData: true,subtree: true,})// 监听窗口大小变化const handleResize = () => {clearTimeout(el._resizeTimer)el._resizeTimer = setTimeout(updateTooltip, 100)}window.addEventListener('resize', handleResize)// 清理函数el._cleanup = () => {observer.disconnect()window.removeEventListener('resize', handleResize)clearTimeout(el._updateTimer)clearTimeout(el._resizeTimer)// 清理 tooltip 相关引用if (el._tooltipContainer) {el._tooltipContainer.remove()}el._tooltipInstance = undefinedel._tooltipContainer = undefined}
}export default {mounted(el: HTMLElement, binding: DirectiveBinding) {setupTruncateTooltip(el, binding)},updated(el: HTMLElement, binding: DirectiveBinding) {// 在 updated 钩子中重新设置,确保数据更新后 tooltip 内容也更新setupTruncateTooltip(el, binding)},unmounted(el: HTMLElement) {// 组件卸载时清理if (el._cleanup) {el._cleanup()}},
}
结果:
cursor不负众望,完成了我想要的结果。还是感叹AI的强大辅助能力,在这里记录一下。