vueuse - 源码学习(useActiveElement)获取当前页面活动元素

1,516 阅读2分钟

useActiveElement: 监听当前页面的活动元素,并且在组件销毁的时候自动取消监听

使用

import { useActiveElement } from '@vueuse/core'

const activeElement = useActiveElement()

watch(activeElement, (el) => {
  console.log('focus changed to', el)
})

API源码

function useActiveElement(options = {}) {
  const { window = defaultWindow } = options;
  const counter = ref(0);
  if (window) {
    useEventListener(window, "blur", () => counter.value += 1, true);
    useEventListener(window, "focus", () => counter.value += 1, true);
  }
  return computed(() => {
    counter.value;
    return window == null ? void 0 : window.document.activeElement;
  });
}

useEventListener

源码中用到另一个方法 useEventListener, 该方法会在组件销毁的时候自动cancel事件监听

function useEventListener(...args) {
  let target;
  let event;
  let listener;
  let options;
  if (isString(args[0])) {
    [event, listener, options] = args;
    target = defaultWindow;
  } else {
    [target, event, listener, options] = args;
  }
  if (!target)
    return noop;
  let cleanup = noop;
  const stopWatch = watch(() => unref(target), (el) => {
    cleanup();
    if (!el)
      return;
    el.addEventListener(event, listener, options);
    cleanup = () => {
      el.removeEventListener(event, listener, options);
      cleanup = noop;
    };
  }, { immediate: true, flush: "post" });
  const stop = () => {
    stopWatch();
    cleanup();
  };
  tryOnScopeDispose(stop);
  return stop;
}

关于blur不会冒泡的处理

useEventListener 在对focus和blur事件的监听过程均采用捕获阶段触发事件,因为blur事件并不会冒泡,在window上监听不到元素的blur事件。

关于事件捕获与冒泡,参考 Javascript事件捕获与事件冒泡

副作用刷新时机

官方文档 Vue 的响应性系统会缓存副作用函数,并异步地刷新它们,这样可以避免同一个“tick” 中多个状态改变导致的不必要的重复调用。在核心的具体实现中,组件的 update 函数也是一个被侦听的副作用。当一个用户定义的副作用函数进入队列时,默认情况下,会在所有的组件 update 执行

useEventListener 中 watch 副作用函数的第三个参数 flush: 'post' 是为了保证事件监听会在组件的update() 函数调用之后再进行绑定,保证了DOM已经渲染出来。

  • flush: 'pre' 默认,所有的自定义副作用函数会在组件update之前执行

  • flush: 'post' 所有的自定义副作用函数会在组件update之后执行,通常为了获取update之后的dom

  • flust: 'sync' 一般不采用,如果这样,这将强制效果始终同步触发。然而,这是低效的,应该很少需要