Event Channel使用全解析与跨页面通信实战经验分享

梓豪 移动 阅读 846
赞 35 收藏
二维码
手机扫码查看
反馈

为什么我选择了 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 的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续我会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论