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' 一般不采用,如果这样,这将强制效果始终同步触发。然而,这是低效的,应该很少需要