- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
onStartTyping是 VueUse 核心包中一个轻量而实用的事件监听组合式函数(Composable):当用户在页面上非可编辑元素(非<input>、<textarea>、contenteditable)处开始输入时触发回调。它的典型应用是“打字即聚焦”——用户无需点击,直接在页面任意位置敲击键盘,即可自动聚焦到指定输入框,非常适合文档检索页、全局搜索框、快捷键导航等场景。读完本文,你将掌握onStartTyping的完整用法、两个可自定义的判定函数(isTypedCharValid与isFocusedElementEditable)以及其底层事件监听与判定逻辑的源码级原理。
背景:为什么需要 onStartTyping
在传统交互中,用户想输入内容必须先点击输入框。而很多追求效率的产品(如全局搜索、命令面板、即时通信的快捷回复)希望做到“按下任意字母键就开始输入”。如果直接在window上监听keydown,又无法区分用户是否正在表单里输入,容易造成误触发——比如用户正在表单中打字,回调却又把焦点抢走。
onStartTyping正是为了解决这一矛盾而生:它内部做了两层判定,只有同时满足“当前聚焦元素不可编辑”和“按下的键是有效字符”时才触发回调,从源头避免了上述误触发问题。该函数的实现源自 react-use),在 Vue 3 生态中以组合式函数的形式重新呈现。
基本用法:打字即聚焦
onStartTyping在文档(默认是document)上监听keydown事件,当用户开始输入时调用你传入的回调。最经典的应用是自动聚焦输入框:
<script setup lang="ts"> import { onStartTyping } from '@vueuse/core' import { useTemplateRef } from 'vue' const input = useTemplateRef('input') onStartTyping(() => { if (!input.value.active) input.value.focus() }) </script> <template> <input ref="input" type="text" placeholder="Start typing to focus"> </template>当页面加载后,用户无需点击输入框,直接在任意位置敲击字母或数字,焦点就会自动跳到这个输入框上。
仓库自带的官方演示提供了一个更贴近真实场景的变体:页面同时放置两个输入框,一个会被自动聚焦,另一个不会:
<script setup lang="ts"> import { onStartTyping } from '@vueuse/core' import { useTemplateRef } from 'vue' const input = useTemplateRef('input') onStartTyping(() => { if (input.value !== document.activeElement) input.value!.focus() }) </script> <template> <note>Type anything</note> <input ref="input" type="text" placeholder="Start typing to focus"> <input type="text" placeholder="Start typing has no effect here"> </template>这里通过input.value !== document.activeElement判断当前焦点是否已在目标输入框上,避免反复调用focus()。两种写法(!input.value.active与document.activeElement比较)等价,读者可以按习惯选用。
可配置选项(Options)
onStartTyping接受第二个参数options,完整签名如下(定义见 packages/core/onStartTyping/index.ts#L48-L51):
| 选项 | 类型 | 默认值 | 作用 |
|---|---|---|---|
isTypedCharValid | (event: KeyboardEvent) => boolean | 内置isTypedCharValid | 判断按下的键是否算作“有效输入字符”,用于过滤触发条件 |
isFocusedElementEditable | () => boolean | 内置isFocusedElementEditable | 判断当前聚焦元素是否可编辑,可编辑时不触发回调 |
document | Document | 全局document(由defaultDocument提供) | 指定自定义document实例,例如在 iframe 或测试环境中使用 |
其中document选项继承了 VueUse 的ConfigurableDocument约定(见 packages/core/_configurable.ts#L11-L16),默认值defaultDocument在服务端渲染(SSR)环境下为undefined,此时onStartTyping会静默跳过监听,天然具备 SSR 安全性(源码中的if (document)守卫)。
自定义有效按键:只允许数字
默认的有效字符是字母(A-Z)和数字(0-9),如果希望收紧或放宽判定,可以传入自定义的isTypedCharValid:
import { onStartTyping } from '@vueuse/core' onStartTyping(handleKey, { // only allow numbers isTypedCharValid: e => /^\d$/.test(e.key) })此时只有数字键会触发回调,例如可以实现“按数字快速切换到某个标签页”的快捷键体验。
自定义可编辑元素判定:排除指定元素
默认逻辑下,只要焦点落在<input>、<textarea>或contenteditable元素上,回调就不会触发。如果你希望某些特殊的可编辑元素也能触发回调(例如一个 id 为targetInput的输入框专门用于全局快捷键),可以覆盖isFocusedElementEditable:
import { isFocusedElementEditable as defaultEditable, onStartTyping } from '@vueuse/core' onStartTyping(handleKey, { isFocusedElementEditable: () => { const { activeElement } = document // Exclude elements with id 'targetInput' if (activeElement?.id === 'targetInput') return true return defaultEditable() } })这里先对targetInput特殊放行(返回true表示“不视为可编辑元素”,从而允许触发),其余情况回退到默认判定。注意:isFocusedElementEditable与isTypedCharValid两个判定函数本身也被 VueUse 作为工具函数导出(见 packages/core/onStartTyping/index.ts#L6-L46),因此可以直接import { isFocusedElementEditable, isTypedCharValid } from '@vueuse/core'复用,这正是上面的defaultEditable引用方式。
触发条件:回调何时被调用
onStartTyping的回调只在以下三个条件同时满足时触发(官方文档明确说明,逻辑见 packages/core/onStartTyping/index.ts#L63-L67):
- 当前没有可编辑元素被聚焦——即焦点不在
<input>、<textarea>或contenteditable元素上; - 按下的键是字母或数字(A-Z、0-9,含小键盘数字);
- 没有按住修饰键——即 Ctrl、Alt、Meta 均未按下。
这三条规则共同保证了:用户在表单里输入时不会误触发回调,使用浏览器快捷键或应用内快捷键(如 Ctrl+S、Ctrl+C)时也不会触发回调。只有当用户真的“想在一个空白的页面上开始打字”时,回调才被触发。
源码原理:两层判定 + 事件监听
第一层判定:当前聚焦元素是否可编辑
isFocusedElementEditable(源码)的实现逻辑如下:
export function isFocusedElementEditable(): boolean { const { activeElement, body } = document if (!activeElement) return false // If not element has focus, we assume it is not editable, too. if (activeElement === body) return false // Assume <input> and <textarea> elements are editable. switch (activeElement.tagName) { case 'INPUT': case 'TEXTAREA': return true } // Check if any other focused element id editable. return activeElement.hasAttribute('contenteditable') }判定步骤可以拆解为:
- 没有任何元素获得焦点(
activeElement为空),视为不可编辑,返回false——此时允许触发回调; - 焦点在
body上,等同于“没有焦点”,同样返回false; - 焦点元素的标签是
INPUT或TEXTAREA,直接视为可编辑,返回true; - 其他元素则检查是否带有
contenteditable属性,带则视为可编辑。
注意:按<kbd>Tab</kbd>之后焦点会落到<body>上,此时isFocusedElementEditable返回false,因此用户按 Tab 聚焦到页面后直接打字同样可以触发回调。
第二层判定:按键是否为有效字符
isTypedCharValid(源码)基于KeyboardEvent的keyCode与修饰键状态进行判定:
export function isTypedCharValid({ keyCode, metaKey, ctrlKey, altKey, }: KeyboardEvent): boolean { if (metaKey || ctrlKey || altKey) return false // 0...9 if ((keyCode >= 48 && keyCode <= 57) || (keyCode >= 96 && keyCode <= 105)) return true // A...Z if (keyCode >= 65 && keyCode <= 90) return true // All other keys. return false }有效字符的keyCode区间如下:
| 字符范围 | keyCode 区间 | 说明 |
|---|---|---|
0–9(主键盘数字行) | 48 – 57 | 主键盘上方数字键 |
0–9(小键盘) | 96 – 105 | 小键盘数字键 |
A–Z | 65 – 90 | 字母键(大小写共用,不区分) |
同时,metaKey、ctrlKey、altKey任一为真则直接返回false。这意味着 Ctrl+S、Ctrl+C、Alt+Tab、Cmd+Space 等快捷键组合都不会触发回调;而 Shift 未被列入修饰键黑名单,所以“按住 Shift 输入大写字母”依然可以正常触发。
事件监听与清理
onStartTyping的主体实现非常简洁(packages/core/onStartTyping/index.ts#L60-L70):
export function onStartTyping(callback: (event: KeyboardEvent) => void, options: OnStartTypingOptions = {}) { const { document = defaultDocument, isTypedCharValid: isTypedCharValidFn = isTypedCharValid, isFocusedElementEditable: isFocusedElementEditableFn = isFocusedElementEditable } = options const keydown = (event: KeyboardEvent) => { if (!isFocusedElementEditableFn() && isTypedCharValidFn(event)) { callback(event) } } if (document) useEventListener(document, 'keydown', keydown, { passive: true }) }几个值得注意的实现细节:
- 通过解构默认值的方式,把用户自定义的
isTypedCharValid/isFocusedElementEditable与内置实现无缝衔接,未传时回退到内置函数; - 底层复用
useEventListener在document上监听keydown事件(packages/core/useEventListener/index.ts),并标记为passive: true,避免阻塞滚动等默认行为; - 监听器的注册与清理由
useEventListener内部的watchImmediate与onCleanup机制管理,组件卸载时自动移除监听,无需手动清理; - 从调用链上看,
useEventListener默认监听目标是defaultWindow,而这里显式传入了document,事件绑定在 Document 级别,页面上任何位置的按键都会被捕获。
测试验证:行为被完整覆盖
仓库为onStartTyping编写了浏览器端测试(packages/core/onStartTyping/index.browser.test.ts),可以视为对上述判定规则的实证:
- 字母触发:依次模拟输入 A-Z 共 26 个字母,断言回调被调用 26 次;
- 数字触发:依次模拟主键盘 0-9 与小键盘 0-9,断言回调被调用 19 次(测试注释说明因小键盘某键行为差异存在 -1 的余量);
- 非法字符不触发:将箭头键(keyCode 37-40)与功能键(keyCode 112 及以上)输入到
<input>元素中,断言回调一次都不触发——因为此时焦点在可编辑的<input>上,第一层判定已经拦截。
这些用例正好覆盖了文档“How It Works”一节描述的三大触发条件,感兴趣的读者可以直接阅读测试文件对照验证。
与 onKeyStroke 的定位区别
VueUse 中还有一个功能更通用的键盘监听函数onKeyStroke(见 packages/core/onKeyStroke/index.md):它按指定按键(如'ArrowDown'、['s', 'S'])监听keydown/keyup/keypress事件,适合实现方向键导航、快捷键绑定等精确按键需求。
而onStartTyping的定位是**“检测用户开始输入这个动作本身”**——它不关心具体按了哪个键,只关心“是否是可编辑区域外的有效输入”。两者的关系可以概括为:
onKeyStroke:面向“具体按键”的监听,精准匹配键位;onStartTyping:面向“输入意图”的监听,判定“是否在打字”。
选择时根据场景判断:做快捷键绑定用onKeyStroke,做“打字即聚焦/打字即搜索”用onStartTyping。
小结
onStartTyping是一个小而美的 VueUse 组合式函数:约 70 行源码(packages/core/onStartTyping/index.ts),通过“可编辑元素判定 + 有效字符判定”两层过滤,优雅地实现了“在页面上任意位置开始打字即触发”的交互。它通过@vueuse/core入口统一导出(packages/core/index.ts#L10),支持通过 options 覆盖两个判定函数以满足自定义规则,并且天然兼容 SSR。无论是全局搜索框的自动聚焦,还是游戏页面的“按任意键开始”,它都能以几行代码干净落地。
- 前端
【免费下载链接】vueuse
Collection of essential Vue Composition Utilities for Vue 3
相关推荐
VueUse onStartTyping 实战:让用户在 AIRI 任意界面开始按键即自动聚焦输入框
VueUse onStartTyping 实战:让用户在 AIRI 任意界面开始按键即自动聚焦输入框 onStartTyping 是 VueUse 面向 Vue
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染react-use 的 useStartTyping:监听用户开始打字并自动聚焦默认输入框的传感器 Hook
react use 的 useStartTyping:监听用户开始打字并自动聚焦默认输入框的传感器 Hook useStartTyping 是 react us
前端airi × VueUse useIntersectionObserver 实战:元素可见性监听的 Composable、指令与类型声明详解
airi × VueUse useIntersectionObserver 实战:元素可见性监听的 Composable、指令与类型声明详解 在 airi 这类
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考