读v-click-outside源码

v-click-outside介绍

v-click-outside github地址

使用在Vue中的插件,一个附加的指令插件,作用是:

  1. 点击指定元素外的区域可以触发事件
  2. 可以绑定多个元素,一个元素上可以绑定多个事件
  3. 有中间件的概念,其实也就是个拦截器

核心实现代码

function onEvent({ el, event, handler, middleware }) {
  // 判断点击事件触发的元素(event.target)是否是绑定元素(el)的子元素
  const isClickOutside = event.target !== el && !el.contains(event.target)
  // 如果不是外部则不触发
  if (!isClickOutside) {
    return
  }
  // middleware是中间件(拦截器),根据返回值可以决定是否拦截,同时还会执行middleware内部的代码
  if (middleware(event, el)) {
    // 绑定的点击元素外区域的触发函数
    handler(event, el)
  }
}

创建触发事件的实例

对其中的参数和格式做处理,为简化后续创建,处理后是一个对象,里面包含el当前绑定元素和eventHandlers绑定事件的数组列表

eventHandlers中每个对象又包含event事件名和handler事件处理函数

  • el:绑定事件的元素
  • events:数组,可以绑定多个事件,比如:blur,foucs等,默认是click(PC),移动端是click和tap
  • handler:绑定的事件触发的函数
  • middleware:中间件
function createInstance({ el, events, handler, middleware }) {
  return {
    el,
    eventHandlers: events.map((eventName) => ({
      event: eventName,
      handler: (event) => onEvent({ event, el, handler, middleware }),
    })),
  }
}

初始化实例并绑定

这里面有2个小细节:

  1. 采用定时器注册事件,实现类似异步注册,提高效率
  2. 将创建好的实例存放在数组缓存为后续注销和修改使用
function bind(el, { value }) {
  // 处理参数和初始值问题
  const { events, handler, middleware, isActive } = processDirectiveArguments(value)
  // 是否可用的开关
  if (!isActive) {
    return
  }
  // 初始化实例
  const instance = createInstance({ el, events, handler, middleware })
  // 根据初始化后的实例,去循环绑定指定的事件和事件处理函数
  instance.eventHandlers.forEach(({ event, handler }) =>
    setTimeout(() => document.addEventListener(event, handler), 0),
  )
  // 存放在数组缓存中
  directive.instances.push(instance)
}

注销已初始化的实例

有两点注意:

  1. 注销事件时,不能用setTimeout,因为需要同步全部注销完后做清除缓存实例的动作
  2. removeEventListener中的处理函数必须用addEventListener中的处理函数,所以这里缓存就起到作用了,如果只是单纯的函数一样是没办法注销的
function removeInstance(el) {
  // 读取缓存中el的实例
  const instanceIndex = directive.instances.findIndex((instance) => instance.el === el)
  // 如果不存在实例则停止注销
  if (instanceIndex === -1) {
    // Note: This can happen when active status changes from false to false
    return
  }
  // 缓存中el的实例
  const instance = directive.instances[instanceIndex]
  // 循环注销
  instance.eventHandlers.forEach(({ event, handler }) =>
    document.removeEventListener(event, handler),
  )
  // 清除缓存中的注销实例
  directive.instances.splice(instanceIndex, 1)
}

解读源码的原因

由于v-click-outside只能在Vue中以指令的,但是有时候可能用不了指令,比如动态生成的元素,也有可能不是在Vue中想要用到,所以通过读源码开发一个原生js库

最后发布了:

outside-click-js npm库

outside-click-js github地址

你可能感兴趣的:(读v-click-outside源码)