Vant Weapp Stepper 步进器组件完全指南:从基础用法到源码级原理剖析
【免费下载链接】vant-weapp 轻量、可靠的小程序 UI 组件库 项目地址: https://gitcode.com/gh_mirrors/va/vant-weapp
Stepper 步进器是 Vant Weapp(轻量、可靠的小程序 UI 组件库)中用于在指定范围内输入、调整数字的核心组件,由增加按钮、减少按钮和输入框三部分组成。本文基于 packages/stepper/README.md 完整梳理其引入方式、全部属性与事件 API,并深入 packages/stepper/index.ts 源码,剖析数值格式化、范围钳制、长按连续触发等底层实现原理,帮助你从"会用"进阶到"懂原理",在实际小程序项目中灵活应对步进器的各种定制需求。
组件介绍与引入
步进器由增加按钮、减少按钮和输入框组成,用于在一定范围内输入、调整数字。它是电商购物车数量、表单数字项等场景中的高频组件。
在 app.json 或对应页面的 index.json 中声明组件(完整引入方式可参考 快速上手):
"usingComponents": {
"van-stepper": "@vant/weapp/stepper/index"
}
组件本质上是微信小程序自定义组件,其 index.json 仅声明 "component": true(见 packages/stepper/index.json)。组件实现通过 VantComponent 工厂封装,该工厂在 packages/common/component.ts 中将 props、watch、methods 等配置映射为微信原生 Component 的 properties、observers、methods,并自动注入 wx://form-field 表单行为——这意味着步进器可以直接参与表单提交,这正是 name 属性存在的意义。
代码演示:九种核心用法
以下用法与官方 Demo(packages/stepper/demo/index.wxml)一一对应,均可直接复制运行。
基础用法
通过 value 设置输入值,通过 change 事件监听输入值变化:
<van-stepper value="{{ 1 }}" bind:change="onChange" />
Page({
onChange(event) {
console.log(event.detail);
},
});
change 事件的回调参数 event.detail 为当前输入值(数字类型),具体见下文事件表。
步长设置
通过 step 属性设置每次点击增加或减少按钮时变化的值,默认为 1:
<van-stepper value="{{ 1 }}" step="2" />
源码中步长参与加减运算的位置在 packages/stepper/index.ts 的 onChange 方法:const diff = type === 'minus' ? -this.data.step : +this.data.step;。
限制输入范围
通过 min 和 max 属性限制输入值的范围:
<van-stepper value="{{ 5 }}" min="5" max="8" />
注意:min、max、step 等属性的类型均为 string | number,传字符串(如 "5")与传数字(如 5)效果一致,源码内部统一通过 + 一元运算符转为数字比较。min 默认值为 1,max 默认值为 Number.MAX_SAFE_INTEGER(见 packages/stepper/index.ts 中 props 定义),即默认不设上限。
限制输入整数
设置 integer 属性后,输入框将限制只能输入整数:
<van-stepper value="{{ 1 }}" integer />
integer 属性在 filter 方法中生效:当输入值包含小数点时,直接截取小数点前的整数部分(value.split('.')[0])。同时,WXML 中输入框的 type 会根据 integer 在 number(整数键盘)与 digit(带小数点键盘)之间切换(见 packages/stepper/index.wxml)。
禁用状态
通过设置 disabled 属性来禁用步进器,禁用状态下无法点击按钮或修改输入框:
<van-stepper value="{{ 1 }}" disabled />
disabled 会同时作用于按钮(WXML 中 van-stepper__minus / van-stepper__plus 增加 disabled 修饰类)和输入框(disabled 与 disableInput 会同步传递给 <input>)。
关闭长按
通过设置 long-press 属性决定步进器是否开启长按手势,默认开启:
<van-stepper value="{{ 1 }}" long-press="{{ false }}" />
注意 long-press 的默认值为 true,若要关闭必须显式传入 false。
固定小数位数
通过设置 decimal-length 属性可以保留固定的小数位数:
<van-stepper value="{{ 1 }}" step="0.2" decimal-length="{{ 1 }}" />
decimal-length 不传时按实际值显示;传入后内部使用 toFixed(decimalLength) 强制格式化输出。
异步变更
如果需要异步地修改输入框的值,可以设置 async-change 属性,并在 change 事件中手动修改 value:
<van-stepper value="{{ value }}" async-change bind:change="onChange" />
Page({
data: {
value: 1,
},
onChange(event) {
Toast.loading({ forbidClick: true });
setTimeout(() => {
Toast.clear();
this.setData({ value: event.detail });
}, 500);
},
});
该场景典型应用于"需要向服务端校验或提交后再更新本地值"的业务,官方 Demo 的完整实现见 packages/stepper/demo/index.ts。异步模式的底层逻辑在 emitChange 中:当 asyncChange 为 true 时,组件不会主动 setData 更新 currentValue,仅向外抛出 change 事件,由业务方决定何时把新值写回 value。
自定义大小
通过 input-width 属性设置输入框宽度,通过 button-size 属性设置按钮大小和输入框高度:
<van-stepper value="{{ 1 }}" input-width="40px" button-size="32px" />
input-width 默认值为 32px,button-size 默认值为 28px,二者默认单位为 px。尺寸最终由 packages/stepper/index.wxs 中的 buttonStyle / inputStyle 计算:按钮宽高均为 buttonSize,输入框宽度为 inputWidth、高度与按钮一致,保证三者视觉对齐。
圆角风格(Demo 补充)
除了 README 列出的用法,官方 Demo 还展示了 theme="round" 圆角风格:
<van-stepper value="{{ 1 }}" theme="round" button-size="22px" />
该样式在 packages/stepper/index.less 的 &--round 中实现:按钮变为圆形、加号按钮使用主题色填充、减号按钮为白底描边,圆角风格下输入框背景透明。
API 详解
Props 属性表
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| name | 在表单内提交时的标识符 | string | - |
| value | 输入值 | string | number | 最小值 |
| min | 最小值 | string | number | 1 |
| max | 最大值 | string | number | -(源码中为 Number.MAX_SAFE_INTEGER) |
| step | 步长 | string | number | 1 |
| integer | 是否只允许输入整数 | boolean | false |
| disabled | 是否禁用 | boolean | false |
| disable-input | 是否禁用输入框 | boolean | false |
| async-change | 是否开启异步变更,开启后需要手动控制输入值 | boolean | false |
| input-width | 输入框宽度,默认单位为 px | string | number | 32px |
| button-size | 按钮大小,默认单位为 px,输入框高度会和按钮大小保持一致 | string | number | 28px |
| show-plus | 是否显示增加按钮 | boolean | true |
| show-minus | 是否显示减少按钮 | boolean | true |
| decimal-length | 固定显示的小数位数 | number | - |
| theme | 样式风格,可选值为 round | string | - |
| disable-plus | 是否禁用增加按钮 | boolean | - |
| disable-minus | 是否禁用减少按钮 | boolean | - |
| long-press | 是否开启长按手势 | boolean | true |
always-embed(v1.9.3 起) | 强制 input 处于同层状态,默认 focus 时 input 会切到非同层状态(仅在 iOS 下生效) | boolean | false |
几个容易被忽略的细节:
show-plus/show-minus控制按钮显隐:在 packages/stepper/index.wxml 中分别以wx:if="{{ showMinus }}"与wx:if="{{ showPlus }}"实现,可组合出"只增不减"或"只减不增"的形态。disable-plus/disable-minus与disabled相互独立,仅禁用单个方向按钮。结合isDisabled方法可看出,按钮禁用逻辑为:disabled || disablePlus || +currentValue >= +max(加号)、disabled || disableMinus || +currentValue <= +min(减号),即达到边界值时按钮自动进入禁用态,此时点击会触发overlimit事件而非change。always-embed用于解决 iOS 上输入框 focus 时从同层切换到非同层引起的布局抖动问题,仅在 iOS 生效。
Events 事件表
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| bind:change | 当绑定值变化时触发的事件 | event.detail: 当前输入的值(数字类型) |
| bind:overlimit | 点击不可用的按钮时触发 | -(源码中携带 type:'plus' 或 'minus') |
| bind:plus | 点击增加按钮时触发 | - |
| bind:minus | 点击减少按钮时触发 | - |
| bind:focus | 输入框聚焦时触发 | -(透传 input 的 event.detail) |
| bind:blur | 输入框失焦时触发 | -(源码中携带 value: +data.newValue) |
Slot 插槽
| 名称 | 说明 |
|---|---|
| plus | 加号按钮 |
| minus | 减号按钮 |
按钮内部均预留了 <slot>(见 packages/stepper/index.wxml),可通过自定义插槽内容完全替换默认的 + / - 图形,例如放置自定义图标。插槽为空的按钮会通过 CSS 伪元素 ::before / ::after 绘制默认加号、减号线条(见 packages/stepper/index.less)。
外部样式类
| 类名 | 说明 |
|---|---|
| custom-class | 根节点样式类 |
| input-class | 输入框样式类 |
| plus-class | 加号按钮样式类 |
| minus-class | 减号按钮样式类 |
外部样式类在源码中通过 classes: ['input-class', 'plus-class', 'minus-class'] 声明,custom-class 则由 VantComponent 工厂自动追加(见 packages/common/component.ts),它们会渲染到 WXML 对应节点的 class 上,供页面侧覆盖样式。
源码级原理剖析
数值处理的完整流水线:filter → format → emitChange
步进器所有数值操作都经过 filter、format 两道关卡,最终由 emitChange 输出(packages/stepper/index.ts):
filter(过滤非法字符):先用正则/[^0-9.-]/g剔除所有非数字、小数点、负号的字符;若开启了integer且值包含小数点,则截断为整数部分。format(格式化):先对safeValue做范围钳制Math.max(Math.min(max, +safeValue), min),将值强制限制在[min, max]区间内;随后若配置了decimal-length,用toFixed(decimalLength)补齐小数位。emitChange(变更分发):非异步模式下将格式化后的值写回currentValue并抛出change事件;异步模式下只抛事件、不写值。
值得注意的边界处理:
- 允许输入框临时为空:
onInput中value === ''时直接return,避免用户清空输入框时被强制回填,保证删除操作的流畅性。 - 小数输入体验修复:
emitChange中有一段注释为fix when input 11. parsed to 11, unable to enter decimal,即当用户输入11.时,若直接使用格式化后的值会丢失小数点导致无法继续输入小数位,因此源码采用+value === +newValue ? value : newValue的策略:数值相等时保留用户原始输入字符串(含尾随小数点),否则采用规范化结果。 - 浮点精度处理:
add函数通过放大 10^10 倍取整后再缩小,规避0.1 + 0.2这类经典浮点误差,这也是step="0.2"组合场景能稳定工作的原因。
按钮点击与边界:isDisabled 与 overlimit
按钮点击统一走 onTap(读取 data-type 区分 plus / minus)。onChange 首先调用 isDisabled 判断:若因 disabled、disablePlus / disableMinus 或到达 max / min 边界而禁用,则触发 overlimit 事件并提前返回;否则按方向计算 diff(加号取 +step,减号取 -step),经 format 钳制边界后走 emitChange,最后再抛出 plus / minus 事件。WXML 中按钮的禁用态样式(van-stepper__plus--disabled 等)与 isDisabled 的判定条件保持一致。
长按手势的实现机制
long-press 的默认开启意味着组件内部有一套独立的触摸计时逻辑,核心常量在源码顶部定义:
LONG_PRESS_START_TIME = 600:触摸按下后持续 600ms 判定为长按;LONG_PRESS_INTERVAL = 200:长按生效后每 200ms 自动触发一次步进。
具体流程(见 packages/stepper/index.ts 的 onTouchStart / longPressStep / onTouchEnd):触摸开始时若 longPress 开启则启动 600ms 定时器;600ms 内松手则清空定时器、仅执行单击;超过 600ms 判定为长按,立即触发一次 onChange 并通过 longPressStep 递归注册 200ms 定时器实现连续步进,直到 onTouchEnd 清除定时器。若 long-press 为 false,onTouchStart / onTouchEnd 直接返回,仅保留单击行为。
输入框的受控刷新:watch 与 check
组件通过 watch.value 监听外部 value 变化,调用 observeValue 将格式化后的值同步到内部 currentValue(输入框实际绑定的是 currentValue)。integer、decimalLength、min、max 四个属性均注册了 check 观察者:属性变化时立即用 format 重新校验并修正 currentValue,并用 equal(字符串比较)避免无意义的重复 setData。这种"外部受控 + 内部兜底"的设计,保证了组件在动态修改属性时输入框始终处于合法状态。
测试验证
组件 Demo 通过快照测试保障渲染稳定性:packages/stepper/test/demo.spec.ts 使用 miniprogram-simulate 加载 demo/index 并断言渲染结果与快照一致(快照见 packages/stepper/test/snapshots/demo.spec.ts.snap)。如果你在修改组件或定制样式后需要回归验证,可参考该测试的写法在仓库中运行对应测试用例。
样式定制
步进器的尺寸与颜色大量使用 CSS 变量(见 packages/stepper/index.less),可以在页面级覆盖,例如:
--stepper-background-color:按钮与输入框背景色--stepper-input-width/--stepper-input-height:输入框宽高--stepper-input-font-size/--stepper-input-text-color:输入框字号与文字颜色--stepper-button-icon-color:按钮图标颜色--stepper-border-radius:按钮圆角--stepper-active-color:按钮按压态背景色
加上前文介绍的外部样式类与插槽机制,步进器基本可以无侵入地适配各类业务视觉规范。
总结
Stepper 步进器虽然 API 简洁,但内部凝聚了输入过滤、范围钳制、小数精度、异步受控、长按计时等多重工程细节。掌握本文的属性与事件全表,即可覆盖"限范围、限整数、定步长、保小数、异步提交、自定义尺寸与风格"等绝大多数业务场景;理解其 filter → format → emitChange 流水线与长按机制后,更能举一反三,在遇到深层定制需求时快速定位源码(packages/stepper/index.ts、packages/stepper/index.wxml、packages/stepper/index.wxs)进行精准改造。
【免费下载链接】vant-weapp 轻量、可靠的小程序 UI 组件库 项目地址: https://gitcode.com/gh_mirrors/va/vant-weapp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



