以下是手写的 useFocus 函数的实现,该函数用于跟踪元素的焦点状态并提供主动聚焦/失焦的方法:
import { ref, watch, onUnmounted, unref, type Ref, type ComponentPublicInstance } from 'vue';
export function useFocus(target?: Ref<HTMLElement | ComponentPublicInstance | null>) {
const focused = ref(false);
const element = target ?? ref<HTMLElement | ComponentPublicInstance | null>(null);
const getElement = (): HTMLElement | null => {
const el = unref(element);
return (el as ComponentPublicInstance)?.$el ?? el;
};
const onFocus = () => {
focused.value = true;
};
const onBlur = () => {
focused.value = false;
};
const addListeners = (el: HTMLElement | null) => {
if (el) {
el.addEventListener('focus', onFocus);
el.addEventListener('blur', onBlur);
}
};
const removeListeners = (el: HTMLElement | null) => {
if (el) {
el.removeEventListener('focus', onFocus);
el.removeEventListener('blur', onBlur);
}
};
watch(
() => getElement(),
(newEl, oldEl) => {
removeListeners(oldEl);
addListeners(newEl);
},
{ immediate: true }
);
const focus = () => {
const el = getElement();
el?.focus();
};
const blur = () => {
const el = getElement();
el?.blur();
};
onUnmounted(() => {
removeListeners(getElement());
});
return {
focused,
focus,
blur,
element: element as Ref<HTMLElement | ComponentPublicInstance | null>,
};
}
使用示例:
<template>
<!-- 使用返回的 element 绑定模板 -->
<input v-if="!customEl" :ref="element" />
<!-- 使用自定义 ref 绑定 -->
<textarea v-if="customEl" :ref="el"></textarea>
<p>Focused: {{ focused }}</p>
<button @click="focus">手动聚焦</button>
<button @click="blur">手动失焦</button>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useFocus } from './useFocus';
// 使用方式 1:自动绑定
const { focused, element, focus, blur } = useFocus();
// 使用方式 2:自定义元素引用
const el = ref<HTMLElement | null>(null);
const { focused, focus, blur } = useFocus(el);
const customEl = ref(false);
</script>
实现细节说明:
-
响应式状态:
focused: 使用ref存储当前焦点状态element: 处理传入的 target 或创建默认 ref
-
元素处理:
getElement()方法处理 Vue 组件实例,自动获取其根 DOM 元素- 使用
unref()处理可能被嵌套的 ref
-
事件监听:
- 通过
watch监听元素变化,自动更新事件监听器 - 在卸载时使用
onUnmounted清理事件监听
- 通过
-
主动控制:
- 提供
focus()和blur()方法手动控制焦点 - 兼容常规 HTML 元素和 Vue 组件
- 提供
-
类型安全:
- 完整 TypeScript 类型标注
- 支持
HTMLElement和 Vue 组件实例