# Tap 事件
# 什么是 tap 事件
tap 事件表示一次用户的"点击"操作:
- 移动端:手指按下后抬起
- PC / 开发者工具:鼠标按下后抬起
tap 事件是基于 Web 标准的 touch 事件封装而来。与原生 click 事件不同,tap 没有 300ms 的延迟,响应更快。
在 WXML 中,通过 bindtap 或 catchtap 绑定 tap 事件的处理函数:
<view bindtap="handleTap">点击我</view>
<button catchtap="handleButtonTap">按钮</button>
Page({
handleTap(e) {
console.log('触发了 tap 事件', e)
},
handleButtonTap(e) {
console.log('按钮被点击', e)
}
})
tap 事件对象中包含点击的位置等信息,具体字段请参考事件系统。
# 点击态
# 什么是点击态
部分敏感 API(如 wx.openSetting、wx.requestSubscribeMessage)必须由用户主动点击触发,不允许在无用户交互的情况下被调用。基础库通过点击态机制来实现这一限制:只有当代码运行在用户点击所产生的执行上下文中时,这些 API 才能被成功调用。
简单来说:用户点了 → 你的事件回调里有点击态 → 可以调用敏感 API。
# 点击态的获取
以下场景中,代码执行时具有点击态:
# 1. tap 事件回调
用户点击触发的事件处理函数天然具有点击态:
Page({
handleTap() {
// ✅ 此处有点击态
wx.openSetting({})
}
})
<button bindtap="handleTap">打开设置</button>
# 2. 特定 API 的回调
以下 API 的回调中也具有点击态:
| API | 说明 |
|---|---|
wx.showModal | success/complete 回调中具有点击态 |
wx.showActionSheet | success/complete 回调中具有点击态 |
wx.requestPayment | success/complete/fail 回调中具有点击态 |
wx.requestOrderPayment | success/complete/fail 回调中具有点击态 |
wx.showModal({
title: '提示',
content: '是否订阅消息?',
success(res) {
if (res.confirm) {
// ✅ 此处有点击态,可调用敏感 API
wx.requestSubscribeMessage({ tmplIds: ['...'] })
}
}
})
注意:
wx.requestPayment和wx.requestOrderPayment回调中获得的点击态不可用于触发wx.openEmbeddedMiniProgram,详见支付后打开半屏小程序能力的相关调整通知。
# 点击态的传递
在事件回调中调用以下 API 时,如果调用时具有点击态,则该 API 的 success/complete/fail 回调会继承点击态:
wx.requestwx.downloadFilewx.getSetting
这使得"点击后发请求,请求完成后调用敏感 API"这一常见模式能够正常工作:
Page({
handleTap() {
// ✅ 有点击态
wx.request({
url: 'https://example.com/api/check',
success(res) {
// ✅ 点击态被传递到此处
wx.openSetting({})
}
})
}
})
点击态的传递支持链式调用:
Page({
handleTap() {
// ✅ 有点击态
wx.request({
url: 'https://example.com/api/step1',
success() {
// ✅ 有点击态
wx.request({
url: 'https://example.com/api/step2',
success() {
// ✅ 有点击态,仍可调用敏感 API
wx.requestSubscribeMessage({ tmplIds: ['...'] })
}
})
}
})
}
})
# 点击态的生命周期
点击态在当前宏任务结束时自动失效,而不是被某个 API 调用后立即消耗。这意味着在同一个同步执行流中,可以多次调用需要点击态的 API:
Page({
handleTap() {
// ✅ 有点击态
wx.openSetting({}) // 成功调用
wx.requestSubscribeMessage({ // 仍然成功,点击态未被消耗
tmplIds: ['...']
})
wx.request({
url: 'https://example.com/api',
success() {
// ✅ 有点击态(通过 wx.request 的传递机制)
}
})
}
})
但 setTimeout / setInterval 会创建新的宏任务,点击态不会延续到新的宏任务中:
Page({
handleTap() {
// ✅ 有点击态
setTimeout(() => {
// ❌ 新的宏任务,点击态已失效
wx.openSetting({}) // 调用失败
}, 0)
setInterval(() => {
// ❌ 新的宏任务,点击态已失效
}, 1000)
}
})
微任务(microtask)在当前宏任务内执行,因此点击态保持有效:
Page({
async handleTap() {
// ✅ 有点击态
await someAsyncOperation()
// ✅ 点击态仍然有效(Promise/async/await 属于微任务)
wx.openSetting({}) // 成功调用
}
})
# 需要点击态的 API 列表
以下 API 必须在具有点击态的上下文中调用,否则会调用失败或功能降级:
| API | 说明 |
|---|---|
wx.openSetting | 打开设置页面 |
wx.requestSubscribeMessage | 请求订阅消息 |
wx.requestSubscribeDeviceMessage | 请求订阅设备消息 |
wx.exitMiniProgram | 退出小程序 |
wx.getUserProfile | 获取用户信息 |
wx.openCustomerServiceChat | 打开客服会话 |
wx.addPhoneCalendar | 添加手机日历事件 |
wx.addPhoneRepeatCalendar | 添加手机重复日历事件 |
wx.chooseLicensePlate | 选择车牌号 |
wx.shareFileMessage | 分享文件到聊天 |
wx.shareVideoMessage | 分享视频到聊天 |
wx.addFileToFavorites | 添加文件到收藏 |
wx.addVideoToFavorites | 添加视频到收藏 |
wx.openSystemBluetoothSetting | 打开系统蓝牙设置 |
wx.openAppAuthorizeSetting | 打开应用权限设置 |
wx.openOfficialAccountArticle | 打开公众号文章 |
wx.openOfficialAccountProfile | 打开公众号资料页 |
wx.openChannelsLiveNoticeInfo | 打开视频号直播预告 |
wx.openInquiriesTopic | 打开问一问话题 |
wx.notifyGroupMembers | 通知群成员 |
wx.shareFileToGroup | 分享文件到群 |
wx.shareImageToGroup | 分享图片到群 |
wx.shareVideoToGroup | 分享视频到群 |
wx.shareToOfficialAccount | 分享到公众号 |
wx.openEmbeddedMiniProgram | 打开半屏小程序 |
VideoContext.startCasting | 开始投屏,拉起半屏搜索设备 |
VideoContext.switchCasting | 切换投屏设备 |
VideoContext.reconnectCasting | 重连投屏设备 |
VideoContext.exitCasting | 退出投屏 |
# 常见错误与排查
在缺少点击态的情况下调用上述 API,会收到如下错误信息:
{apiName}:fail can only be invoked by user TAP gesture.
例如:
// 在 onLoad 中直接调用(无点击态)
Page({
onLoad() {
wx.openSetting({
fail(err) {
console.log(err.errMsg)
// "openSetting:fail can only be invoked by user TAP gesture."
}
})
}
})
# 最佳实践
尽早调用敏感 API:在点击事件回调中尽快调用需要点击态的 API,避免不必要的异步延迟。
避免在 setTimeout 中调用:
setTimeout/setInterval会创建新的宏任务,导致点击态失效。利用点击态传递:如果需要先请求后端再调用敏感 API,使用
wx.request的回调来组织代码,点击态会自动传递到回调中。