手写VueUse的onClickOutside函数

以下是手写 VueUse 的 onClickOutside 函数的实现,采用 Vue 3 组合式 API 风格,包含详细注释和代码:

typescript 复制代码
import { Ref, onMounted, onUnmounted } from 'vue';

/**
 * 检测点击目标元素外部的操作
 * @param targetRef 要监听的目标元素引用
 * @param callback 点击外部时触发的回调函数
 */
export function onClickOutside(
  targetRef: Ref<HTMLElement | null>,
  callback: (event: MouseEvent) => void
) {
  // 事件处理函数
  const listener = (event: MouseEvent) => {
    const target = targetRef.value;
    
    // 确保目标元素存在且事件目标存在
    if (!target || !event.target) return;

    // 判断点击是否发生在目标元素外部
    if (!target.contains(event.target as Node)) {
      callback(event);
    }
  };

  // 组件挂载时添加监听
  onMounted(() => {
    // 使用捕获阶段确保在阻止冒泡的情况下仍能触发
    document.addEventListener('click', listener, { capture: true });
  });

  // 组件卸载时移除监听
  onUnmounted(() => {
    document.removeEventListener('click', listener, { capture: true });
  });
}

使用示例

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue';
import { onClickOutside } from './onClickOutside';

const target = ref<HTMLElement | null>(null);
const isOpen = ref(true);

onClickOutside(target, () => {
  isOpen.value = false;
});
</script>

<template>
  <div v-if="isOpen" ref="target" class="modal">
    点击外部区域关闭我
  </div>
</template>

实现要点解析

  1. 事件阶段处理:

    • 使用 { capture: true } 在捕获阶段监听事件,确保即使其他事件调用了 stopPropagation() 也能触发
  2. 类型安全:

    • 使用 TypeScript 类型标注,明确处理 HTMLElement 和事件对象类型
  3. 内存管理:

    • 通过 onUnmounted 自动清理事件监听,避免内存泄漏
  4. 边界情况处理:

    • 检查目标元素是否存在(targetRef.value)
    • 检查事件目标是否存在(event.target)
  5. DOM 判断逻辑:

    • 使用 contains() 方法判断点击目标是否在监听元素内部

扩展功能建议

如需实现更复杂的功能,可以考虑添加以下特性:

  1. 排除元素:允许配置不触发回调的排除元素
  2. 事件类型 :支持监听其他事件类型(如 mousedown)
  3. 响应式配置:通过响应式参数控制监听行为
  4. 立即触发:添加 immediate 选项支持初始状态检测
  5. 条件判断:添加执行回调的条件判断函数
相关推荐
FRing034 分钟前
JavaScript入门到精通二十六(变量提升、函数剩余参数、展开运算符、ES6箭头函数)
开发语言·前端·javascript·es6·箭头函数
用户360055579009 分钟前
Kestrel_VLLM - 15 · decode 优化四连击:19ms → 8ms
前端
IT_陈寒31 分钟前
JavaScript的闭包陷阱,这个坑我躺了三天才爬出来
前端·人工智能·后端
计算机魔术师40 分钟前
Microsoft Research 与合作者证明 31 年历史的 KLS 猜想
前端
weixin_417396831 小时前
桌面云终端PXE启动失败怎么排查?从DHCP Option到镜像下发
开发语言·前端·php·技术·酒店·综合布线·弱电
林恒smileZAZ1 小时前
浏览器为什么加载不了超大图片
前端·vue.js·html5
hhwyqwqhhwy2 小时前
设备树之 provider 和 consumer
java·linux·前端
垆边人似月.3 小时前
Python中如何使用get函数实现频次查询
服务器·前端·python
moshuying3 小时前
DeepSeek开源生产级推理框架DeepInfer:单集群一天处理155万亿token,这意味着什么?
前端
子兮曰4 小时前
PHP漏洞危机,你的网站升级了吗?
前端·后端·php