手写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. 条件判断:添加执行回调的条件判断函数
相关推荐
平头哥~2 小时前
Day 07 _ 那条 1px 的线,为什么在手机上忽粗忽细
前端·css·样式·学习资料
小江的记录本2 小时前
【CSS】CSS 动画:transition、animation、transform、CSS3 新特性(附《思维导图》)
前端·css·安全·spring·前端框架·css3·动画
circuitsosk2 小时前
任务规划器的三种范式对比:ReAct、Plan-and-Execute 与 Tree-of-Thought 在真实业务中的取舍
前端·javascript·python·react.js·llm·ai agent
hzxpaipai3 小时前
企业官网技术架构拆解:前端、后台、数据库、服务器如何协同
前端·数据库·架构
深念Y5 小时前
Lenovo XiaoXinAir 14 — 性能模式切换
前端·网络
计算机魔术师6 小时前
Meta突然杀进第一梯队:一个新模型,把AI推理成本打下来8倍
前端
IT_陈寒6 小时前
Redis缓存雪崩把我坑惨了,这次长记性了
前端·人工智能·后端
风骏时光牛马6 小时前
提示词工程:大模型效能挖掘与指令设计实战
前端
仓三6 小时前
Chrome DevTools MCP 上手:让 Coding Agent 自己调试前端页面
前端·chrome devtools·mcp