一、前言:事件是小程序"活"起来的关键
一个静态的小程序页面只是展示信息,而事件(Event) 才让它具备交互能力。
无论是点击按钮、滑动屏幕,还是页面加载完成,背后都离不开事件驱动。
但很多初学者常遇到这些问题:
- ❓ "为什么 bindtap 不生效?"
- ❓ "如何获取点击的列表项 ID?"
- ❓ "页面什么时候加载数据?onLoad 还是 onShow?"
本文将系统讲解微信小程序的 两大类事件 :
✅ 用户交互事件 (点击、滑动、输入等)
✅ 页面生命周期事件(加载、显示、卸载等)
并附上实战代码与最佳实践,助你写出高响应、低 bug 的小程序!
二、用户交互事件:让页面"可操作"
1. 基础事件绑定语法
在 WXML 中,通过 bind 或 catch 前缀绑定事件:
XML
<!-- 绑定点击事件 -->
<button bindtap="handleClick">点击我</button>
<!-- 阻止冒泡(常用在列表项内按钮) -->
<view bindtap="goDetail">
<button catchtap="likeItem">点赞</button>
</view>
| 前缀 | 作用 |
|---|---|
bind |
正常绑定,事件会冒泡 |
catch |
绑定并阻止冒泡 |
2. 常用用户事件列表
| 事件名 | 触发条件 | 典型场景 |
|---|---|---|
tap |
手指触摸后离开 | 按钮点击、列表项跳转 |
longpress |
长按(>350ms) | 删除、编辑菜单 |
input |
输入框内容变化 | 搜索、表单校验 |
change |
picker / checkbox / radio 值改变 | 选择城市、切换开关 |
scroll |
scroll-view 滚动 | 上拉加载、吸顶导航 |
touchstart/touchmove/touchend |
手势操作 | 自定义滑动删除 |
3. 实战:如何传递参数?
✅ 方法 1:使用 data-* 自定义属性(推荐!)
XML
<!-- WXML -->
<view
wx:for="{{list}}"
wx:key="id"
data-id="{{item.id}}"
data-name="{{item.name}}"
bindtap="onItemClick">
{{item.title}}
</view>
javascript
// JS
Page({
onItemClick(e) {
const { id, name } = e.currentTarget.dataset;
console.log('点击了:', id, name);
// 跳转详情页
wx.navigateTo({ url: `/pages/detail/detail?id=${id}` });
}
})
🔑 关键:使用
e.currentTarget.dataset(不是e.target!)
❌ 错误写法(不要这样做!)
javascript
<!-- 错误:JS 表达式不能直接传参 -->
<button bindtap="handleClick(item.id)">点我</button>
4. 阻止事件冒泡:避免"误触发"
场景:列表项可点击,内部有"收藏"按钮,点击按钮不应触发列表跳转。
javascript
<view class="item" bindtap="goDetail">
商品标题
<button catchtap="toggleFavorite">❤ 收藏</button>
</view>
✅
catchtap阻止了点击事件向上冒泡到父 view。
三、页面生命周期事件:掌控页面"一生"
小程序页面有完整的生命周期,开发者可在关键节点执行逻辑。
核心生命周期函数(按顺序)
| 函数 | 触发时机 | 典型用途 |
|---|---|---|
onLoad |
页面加载时(只执行一次) | 获取 URL 参数、初始化数据 |
onShow |
页面显示/切入前台 | 刷新数据(如购物车数量) |
onReady |
页面初次渲染完成 | 操作 DOM(如 canvas 初始化) |
onHide |
页面隐藏/切入后台 | 暂停音频、保存草稿 |
onUnload |
页面卸载(如 navigateBack) | 清理定时器、取消订阅 |
生命周期流程图
打开页面 → onLoad → onShow → onReady
↗
返回前台 → onShow
↘
切到后台 → onHide
↘
关闭页面 → onUnload
实战对比:onLoad vs onShow
javascript
Page({
// 页面首次加载(带参数)
onLoad(options) {
console.log('onLoad: 获取参数', options.id);
this.fetchProductDetail(options.id);
},
// 每次进入页面都调用(包括从其他页面返回)
onShow() {
console.log('onShow: 刷新购物车图标');
const cartCount = getApp().globalData.cart.length;
this.setData({ cartCount });
}
});
✅ 最佳实践:
- 一次性初始化 →
onLoad- 需要频繁刷新的数据 →
onShow
四、组件事件:父子通信桥梁
除了页面事件,自定义组件也支持事件。
子组件触发事件(child-component.js)
javascript
// components/my-button/my-button.js
this.triggerEvent('myclick', { value: 'hello' });
父页面监听(parent.wxml)
javascript
<my-button bind:myclick="handleChildClick" />
父页面处理(parent.js)
javascript
handleChildClick(e) {
console.log('子组件传来:', e.detail.value); // "hello"
}
💡 这是小程序实现 组件间通信 的标准方式。
五、常见问题与避坑指南
❌ 坑 1:在 onLoad 中更新 UI,但数据未变?
原因 :
onLoad早于setData渲染
解决 :确保数据已准备好再setData
❌ 坑 2:e.target 和 e.currentTarget 混淆
e.target:触发事件的源元素e.currentTarget:绑定事件的当前元素(推荐用这个取 dataset)
❌ 坑 3:忘记解绑定时器或监听器
后果 :内存泄漏、重复执行
解决 :在onUnload中清理
javascript
onUnload() {
clearInterval(this.timer);
this.timer = null;
}
六、性能优化建议
- 避免在事件处理中频繁 setData
→ 合并数据更新,减少渲染次数 - 长列表使用
wx:for+wx:key
→ 提升列表渲染性能 - 复杂手势用
catch阻止冒泡
→ 避免不必要的事件传播
七、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!