Event Channel使用全解析与跨页面通信实战经验分享
为什么我选择了 Event Channel
最近做的一个项目是个商城类的小程序,主流程是商品浏览到下单支付。这个项目有个特殊需求:在商品详情页可以打开一个规格选择的弹窗,用户选完规格后要实时更新父页面的商品信息和价格。
最开始我想用父子组件传值的方式实现,但发现规格选择弹窗其实是个独立页面,传统的 props 和 emit 根本派不上用场。后来想到用全局状态管理,但又觉得为了这么个小功能引入 Vuex 有点杀鸡用牛刀。最后发现了小程序原生提供的 Event Channel,感觉这玩意还挺适合当前场景,就决定试试。
核心代码就这几行
简单来说,Event Channel 提供了一种页面间通信的方式。下面是我实际用到的核心代码:
// 父页面跳转到规格选择页
wx.navigateTo({
url: '/pages/specSelect/specSelect',
events: {
updateProductInfo: function(data) {
console.log('收到子页面数据', data);
this.setData({
selectedSpec: data.spec,
totalPrice: data.price
});
}
},
success: function(res) {
// 通过 eventChannel 向子页面传递初始数据
res.eventChannel.emit('initData', {
productId: this.data.productId,
defaultSpec: this.data.defaultSpec
});
}
});
// 子页面接收和发送数据
Page({
onLoad: function(options) {
const eventChannel = this.getOpenerEventChannel();
// 监听父页面传递的初始化数据
eventChannel.on('initData', (data) => {
console.log('接收到父页面数据', data);
this.setData({
productId: data.productId,
currentSpec: data.defaultSpec
});
});
},
// 当用户选择规格后触发
onSpecChange: function(newSpec) {
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('updateProductInfo', {
spec: newSpec,
price: this.calculatePrice(newSpec)
});
}
});
最大的坑:页面栈管理问题
用了几天之后发现问题来了。当用户快速切换多个商品时,页面栈会变得很乱。比如从商品A打开规格页,然后返回,再打开商品B的规格页,这时候之前的事件监听器竟然还在!导致两个页面的数据互相干扰。
这个问题折腾了我好久,最后的解决方案是在页面卸载时手动清理事件监听:
Page({
onLoad: function(options) {
this.eventChannel = this.getOpenerEventChannel();
this.eventChannel.on('initData', this.handleInitData);
},
handleInitData: function(data) {
// 处理初始化数据
},
onUnload: function() {
// 手动移除监听器
if (this.eventChannel) {
this.eventChannel.off('initData', this.handleInitData);
}
}
});
这里要注意的是,off 方法必须传入和 on 方法相同的回调函数引用,否则无法正确移除监听器。我一开始直接写了个匿名函数,结果怎么都移除不掉,踩了好久的坑。
性能优化上的小技巧
另一个值得注意的地方是数据传输量的控制。最初我在 initData 里一股脑把整个商品详情的数据都传过去了,结果发现页面切换会卡顿。后来改成只传必要的字段,性能明显改善。
还有一点经验:尽量避免频繁地 emit 数据。我遇到过一个情况,用户快速滑动规格选择器时会疯狂触发 change 事件,导致页面响应变慢。解决方法是加了个防抖:
onSpecChange: _.debounce(function(newSpec) {
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('updateProductInfo', {
spec: newSpec,
price: this.calculatePrice(newSpec)
});
}, 200)
回顾与反思
总的来说,Event Channel 在这个项目里表现不错。它最大的优势是轻量、原生支持,不需要额外引入库。对于简单的跨页面通信场景确实很方便。
不过也有些遗憾的地方。首先它的 API 设计有点反直觉,比如必须通过 getOpenerEventChannel 获取实例,而且不能在 App 全局使用。其次就是前面提到的页面栈管理问题,虽然最后解决了,但总觉得不够优雅。
如果让我重来一次,可能会考虑结合 Redux 这样的状态管理工具,虽然前期配置复杂点,但在大型项目中可能更可控。不过对于这种中小型项目,Event Channel 的确是个不错的折中方案。
以上是我个人对这个 Event Channel 的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续我会继续分享这类博客。

暂无评论