手写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. 条件判断:添加执行回调的条件判断函数
相关推荐
不会写DN3 分钟前
从零打造一个丝滑的 Vue 3 返回顶部组件
前端·javascript·vue.js
架构师老Y4 分钟前
010:API网关调试手记:路由、认证与限流的那些坑
开发语言·前端·python
前端老石人5 分钟前
无障碍访问
开发语言·前端·html
黑金IT5 分钟前
AI带‘脑’摒弃前端硬编码实现浏览器自动化系统
前端·人工智能·自动化
榴莲omega5 分钟前
第13天:CSS(二)| Grid 布局完全指南
前端·css·js八股
牧杉-惊蛰6 分钟前
修改表格选中时的背景色与鼠标滑过时的背景色
前端·javascript·css·vue.js·elementui·html
彧翎Pro7 分钟前
前端状态管理进化史:从Redux到Zustand的范式转变
前端·javascript
bjzhang757 分钟前
使用 HTML + JavaScript 实现表格滚动效果
前端·javascript·html·表格滚动效果
ZStack开发者社区13 分钟前
ZSTACK · 答客问 | 高频问题合集
前端·网络·php
林恒smileZAZ5 小时前
Vue<前端页面版本检测>
前端·javascript·vue.js