Vant NumberKeyboard 数字键盘组件完全指南:从基础用法到源码级实现原理
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
导读
本文是 Vant 移动端组件库中NumberKeyboard(数字键盘)组件的完整技术指南,覆盖组件定位、安装注册、六大典型场景的实战用法、全部 Props/Events/Slots API、主题定制方案,并深入NumberKeyboard.tsx、NumberKeyboardKey.tsx与测试用例,剖析按键布局生成、输入事件分发、触摸交互与动画生命周期等底层实现。读完本文,你将能够熟练地在支付金额、身份证号、验证码、密码输入等场景中集成并定制数字键盘,也能理解其内部工作机制,为二次开发与问题排查打下基础。
一、组件定位:虚拟数字键盘的典型应用场景
NumberKeyboard是 Vant 提供的虚拟数字键盘组件,官方文档明确说明它可以配合 PasswordInput 密码输入框组件 或自定义的输入框组件一起使用(参见 README.md)。在移动端 Web 应用中,它主要解决以下问题:
- 调用系统键盘样式不可控、布局突兀;
- 支付、转账等金额输入场景需要定制的数字键盘布局;
- 身份证号输入需要字母 X 键,系统键盘无法定制;
- 安全等级较高的场景(如交易密码)需要打乱数字顺序防止窥探。
组件以van-number-keyboard为标签名对外使用,通过withInstall封装为可全局注册的插件(见 index.ts)。
二、安装与注册
组件通过app.use全局注册:
import { createApp } from 'vue'; import { NumberKeyboard } from 'vant'; const app = createApp(); app.use(NumberKeyboard);除了全局注册,Vant 还支持按需引入等其他注册方式(参考组件注册)。从 index.ts 可以看到,注册后组件即通过declare module 'vue'扩展了GlobalComponents类型声明,模板中可直接使用<van-number-keyboard>并获得完整的类型提示。
三、基础用法:默认键盘与核心事件
默认键盘由数字 1~9、左下角额外按键、0 和右下角删除键组成。官方示例中使用van-cell作为触发入口,监听show状态控制键盘显隐:
<van-cell @touchstart.stop="show = true">弹出默认键盘</van-cell> <van-number-keyboard :show="show" @blur="show = false" @input="onInput" @delete="onDelete" />import { ref } from 'vue'; import { showToast } from 'vant'; export default { setup() { const show = ref(true); const onInput = (value) => showToast(value); const onDelete = () => showToast('delete'); return { show, onInput, onDelete, }; }, };数字键盘提供input、delete、blur三个核心事件,分别对应输入内容、删除内容、失去焦点三种动作(见 README.zh-CN.md)。demo 源码 demo/index.vue 展示了完整的事件接线方式。
点击外部自动收起
默认情况下,点击键盘以外的区域时键盘会自动收起,这是通过hideOnClickOutside属性控制的。中文文档特别提示:通过阻止元素上的touchstart事件冒泡可以避免键盘收起,这就是示例中@touchstart.stop="show = true"的用意。源码中该逻辑由@vant/use的useClickAway组合式函数实现,监听touchstart事件(见 NumberKeyboard.tsx)。
四、六大进阶场景实战
1. 带右侧栏的键盘(金额输入)
将theme属性设置为custom即可展示右侧栏,常用于输入金额的场景。右侧栏由删除键和关闭按钮组成:
<van-number-keyboard :show="show" theme="custom" extra-key="." close-button-text="完成" @blur="show = false" @input="onInput" @delete="onDelete" />从源码renderSidebar可以看出,custom主题下右侧栏纵向排列删除键与关闭按钮,关闭按钮强制使用蓝色主题色(color="blue"),且支持closeButtonLoading加载态(见 NumberKeyboard.tsx)。
2. 身份证号键盘
通过extra-key属性设置左下角按键内容,比如输入身份证号时将extra-key设置为X:
<van-cell plain type="primary" @touchstart.stop="show = true"> 弹出身份证号键盘 </van-cell> <van-number-keyboard :show="show" extra-key="X" close-button-text="完成" @blur="show = false" @input="onInput" @delete="onDelete" />3. 带标题的键盘
通过title属性设置键盘标题,标题栏同时支持左侧插槽title-left与右侧关闭按钮:
<van-cell plain type="primary" @touchstart.stop="show = true"> 弹出带标题的键盘 </van-cell> <van-number-keyboard :show="show" title="键盘标题" extra-key="." close-button-text="完成" @blur="show = false" @input="onInput" @delete="onDelete" />需要说明的是:关闭按钮只在默认主题下出现在标题栏右侧(源码renderTitle中showClose = closeButtonText && theme === 'default');custom主题下的关闭按钮固定位于右侧栏底部。标题栏仅当title、关闭按钮或title-left插槽任一存在时才渲染(见 NumberKeyboard.tsx)。
4. 配置多个额外按键
当theme为custom时,extra-key支持以数组形式配置两个额外按键:
<van-number-keyboard :show="show" theme="custom" :extra-key="['00', '.']" close-button-text="完成" @blur="show = false" @input="onInput" @delete="onDelete" />5. 随机数字键盘(安全场景)
通过random-key-order属性随机排序数字键,常用于安全等级较高的场景(如交易密码防窥探):
<van-cell @touchstart.stop="show = true"> 弹出配置随机数字的键盘 </van-cell> <van-number-keyboard :show="show" random-key-order @blur="show = false" @input="onInput" @delete="onDelete" />注意 demo 源码中随机键盘示例在测试环境下会被跳过(v-if="!isTest"),因为随机顺序会让快照测试不稳定(见 demo/index.vue)。
6. 双向绑定
通过v-model直接绑定当前输入值,用maxlength限制输入长度:
<van-field v-model="value" readonly clickable @touchstart.stop="show = true" /> <van-number-keyboard v-model="value" :show="show" :maxlength="6" @blur="show = false" />import { ref } from 'vue'; export default { setup() { const show = ref(true); const value = ref(''); return { show, value, }; }, };五、API 全量速查
Props
下表完整继承官方文档(README.md),并结合 NumberKeyboard.tsx 中的 props 定义补充说明:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| v-model | 当前输入值 | string | '' |
| show | 是否显示键盘 | boolean | false |
| title | 键盘标题 | string | - |
| theme | 样式风格,可选值为custom | string | default |
| maxlength | 输入值最大长度 | number | string | Infinity |
| transition | 是否开启过场动画 | boolean | true |
| z-index | 键盘 z-index 层级 | number | string | 100 |
| extra-key | 底部额外按键的内容 | string | string[] | '' |
| close-button-text | 关闭按钮文字,空则不展示 | string | - |
| delete-button-text | 删除按钮文字,空则展示删除图标 | string | 删除图标 |
| close-button-loading | 是否将关闭按钮设置为加载中状态,仅在theme="custom"时有效 | boolean | false |
| show-delete-key | 是否展示删除键 | boolean | true |
| blur-on-close | 是否在点击关闭按钮时触发 blur 事件 | boolean | true |
| hide-on-click-outside | 是否在点击外部时收起键盘 | boolean | true |
| teleport | 指定挂载的节点,等同于 Vue Teleport 组件的to属性 | string | Element | - |
| safe-area-inset-bottom | 是否开启底部安全区适配 | boolean | true |
| random-key-order | 是否以随机顺序展示按键 | boolean | false |
几个值得注意的默认值与实现细节:
maxlength源码使用makeNumericProp(Infinity)定义,即默认不限制长度;组件内部按+props.maxlength转为数字比较;z-index通过工具函数getZIndexStyle处理为内联z-index样式(见 utils/format.ts);- 多个布尔属性(
transition、blurOnClose、showDeleteKey、hideOnClickOutside、safeAreaInsetBottom)使用truthProp定义,即默认值为true; teleport直接复用 VueTeleportProps['to']类型,挂载时可继承 attrs(测试should inherit attrs when using teleport prop对此有验证)。
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| input | 点击按键时触发 | key: string |
| delete | 点击删除键时触发 | - |
| close | 点击关闭按钮时触发 | - |
| blur | 点击关闭按钮或非键盘区域时触发 | - |
| show | 键盘完全弹出时触发 | - |
| hide | 键盘完全收起时触发 | - |
Slots
| 名称 | 说明 |
|---|---|
| delete | 自定义删除按键内容 |
| extra-key | 自定义左下角按键内容 |
| title-left | 自定义标题栏左侧内容 |
类型定义
组件导出以下类型定义(见 index.ts):
import type { NumberKeyboardProps, NumberKeyboardTheme } from 'vant';其中NumberKeyboardTheme取值为'default' | 'custom',另外还从 types.ts 导出了NumberKeyboardThemeVars,用于配合 ConfigProvider 进行主题变量定制。
六、源码级原理剖析
1. 按键布局的生成逻辑
按键布局由computed属性keys按主题分支生成(见 NumberKeyboard.tsx):
- 默认主题:先生成 1~9 数字键,再依次追加左下角额外键、
0键、删除键; - custom 主题:根据
extraKey数量分三种情况排布:- 空数组:只追加一个
wider(加宽)的0键; - 单个额外键:追加加宽
0键 + 额外键; - 两个额外键:按「额外键 - 0 - 额外键」的对称布局排列。
- 空数组:只追加一个
wider布局在 index.less 中实现为flex-basis: 66%,即占据两列宽度;randomKeyOrder时使用 Fisher–Yates 洗牌算法(源码内shuffle函数)打乱 1~9 键顺序。
2. 输入处理与事件分发
所有按键的按压最终汇聚到onPress(text, type)(见 NumberKeyboard.tsx),其分发规则是理解组件的关键:
text为空:若是extra类型额外键则触发blur(默认主题下额外键带收起语义);type === 'delete':触发delete事件,并同步截断modelValue末位字符(value.slice(0, -1));type === 'close':走onClose,触发close事件,并在blurOnClose为true时追加触发blur;- 其余按键:当
value.length < +props.maxlength时触发input并拼接modelValue——超过maxlength后输入会被静默忽略,这一点由测试用例should limit max length of modelValue验证。
3. 按键的触摸交互
单个按键NumberKeyboardKey(见 NumberKeyboardKey.tsx)使用useTouch组合式函数处理触摸事件:
touchstart时置active状态并添加van-key--active高亮类;touchend时若仍处于 active 状态,则触发press事件;若手指在touchmove中滑出按键(有移动方向),则取消高亮且不触发 press,避免误触;- 无插槽内容时通过
preventDefault消除 iOS Safari 上约 300ms 的点击延迟(源码注释引用了 vant-ui 的 issue #6836); - 删除键与额外键未提供自定义内容时,分别渲染内置的 SVG
DeleteIcon与CollapseIcon;关闭按钮在loading状态下渲染Loading组件。
4. 显隐动画与生命周期事件
键盘整体包裹在Transition中,动画名为van-slide-up(见 NumberKeyboard.tsx):
- 默认
transition: true时,show/hide事件在animationend时触发(键盘完全弹出/完全收起后); - 当
transition: false时,组件通过watch监听show变化直接触发show/hide事件,测试用例should emit show/blur event when visibility changed and transition is disabled验证了该分支。
5. 安全区适配与层级
- 键盘容器
position: fixed固定于底部(见 index.less),默认padding-bottom: 22px预留安全区;safe-area-inset-bottom为false时添加van-number-keyboard--unfit修饰类取消该内边距; - 容器样式同时接收透传的 attrs(
inheritAttrs: false后手动展开{...attrs}),因此teleport挂载后仍可正确继承外部 class 等属性。
七、主题定制:CSS 变量
组件在 index.less 中声明了全部 CSS 变量,可通过 ConfigProvider(参考 ConfigProvider 组件)或直接覆盖实现主题定制:
| 名称 | 默认值 | 描述 |
|---|---|---|
| --van-number-keyboard-background | var(--van-gray-2) | 键盘背景色 |
| --van-number-keyboard-key-height | 48px | 按键高度 |
| --van-number-keyboard-key-font-size | 28px | 按键字号 |
| --van-number-keyboard-key-active-color | var(--van-gray-3) | 按键按压态颜色 |
| --van-number-keyboard-key-background | var(--van-background-2) | 按键背景色 |
| --van-number-keyboard-delete-font-size | var(--van-font-size-lg) | 删除键字号 |
| --van-number-keyboard-title-color | var(--van-gray-7) | 标题颜色 |
| --van-number-keyboard-title-height | 34px | 标题栏高度 |
| --van-number-keyboard-title-font-size | var(--van-font-size-lg) | 标题字号 |
| --van-number-keyboard-close-padding | 0 var(--van-padding-md) | 关闭按钮内边距 |
| --van-number-keyboard-close-color | var(--van-primary-color) | 关闭按钮颜色 |
| --van-number-keyboard-close-font-size | var(--van-font-size-md) | 关闭按钮字号 |
| --van-number-keyboard-button-text-color | var(--van-white) | 关闭/删除按钮文字颜色 |
| --van-number-keyboard-button-background | var(--van-primary-color) | 关闭/删除按钮背景色 |
| --van-number-keyboard-z-index | 100 | 键盘 z-index |
此外,index.less 还内置了.van-theme-dark暗黑主题变量覆盖,启用 Vant 暗黑模式时键盘背景、按键背景与按压态颜色会自动切换为深色系。
八、测试验证与常见问题
测试覆盖
组件在 test/index.spec.ts 中有超过 20 个用例,覆盖以下关键行为,可作为功能清单的权威佐证:
- 点击数字键触发
input并同步update:modelValue; - 点击删除键触发
delete;点击收起键触发blur;点击关闭按钮触发close+blur; maxlength限制输入长度;random-key-order打乱顺序(断言 9 次输入与顺序键不完全一致);show-delete-key控制删除键渲染;close-button-loading渲染加载图标;hide-on-click-outside控制点击外部是否 blur;blur-on-close控制关闭时是否 blur;- 三个插槽(
delete、extra-key、title-left)的渲染;teleport挂载后 attrs 继承。
同时 test/demo.spec.ts 通过快照测试保证 demo 渲染结构稳定。
常见问题
在桌面端无法操作组件?由于键盘交互基于touchstart/touchend触摸事件,桌面端鼠标操作可能失效,可参考 Vant 文档中桌面端适配一节的说明(如使用 vant-touch-emulator 等方案)解决。
结语
NumberKeyboard 是 Vant 中一个「麻雀虽小、五脏俱全」的组件:从按键布局生成、触摸事件管理、动画生命周期到安全区与暗黑模式适配,均有清晰、可测试的实现。结合本文的实战示例与源码解析,你可以根据业务需要自由组合theme、extra-key、random-key-order、teleport等能力,快速搭建安全合规、体验一致的数字输入场景。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考