评论

小程序EventChannel的一些理解和使用实践

小程序EventChannel的一些理解和使用实践

EventChannel什么

通过官方文档和示例,实际可以发现EventChannel借助wx.navigateTo方法,在两个页面之间构建起了数据通道,互相可以通过“派发事件”及“注册这些事件的监听器”来实现基于事件的页面通信。

具体看下下面的官方示例:

wx.navigateTo({
  url: 'test?id=1',
  events: {
    // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
    acceptDataFromOpenedPage: function(data) {
      console.log(data)
    },
    someEvent: function(data) {
      console.log(data)
    }
    ...
  },
  success: function(res) {
    // 通过 eventChannel 向被打开页面传送数据
    res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })
  }
})

events: 注册将在目标页面触发(派发)的同名事件的监听器

success:跳转后进行可通过res.eventChannel 触发自定义事件

//test.js
Page({
  onLoad: function(option){
    console.log(option.query)
    const eventChannel = this.getOpenerEventChannel()
    eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'});
    eventChannel.emit('someEvent', {data: 'test'});
    // 监听 acceptDataFromOpenerPage 事件,获取上一页面通过 eventChannel 传送到当前页面的数据
    eventChannel.on('acceptDataFromOpenerPage', function(data) {
      console.log(data)
    })
  }
})

通过this.getOpenerEventChannel()获取eventChannel对象,

使用eventChannel.emit() 可以在任意时刻触发(派发)事件。当前页面即可执行对应的事件处理函数。

使用eventChannel.on() 即可监听上个页面emit的事件。

使用示例

  1. A页面为订单页面,B页面为填写发票信息页面,
  2. A页面跳转B页面携带开票金额(amount),
  3. A页面如果已填写,再次跳转B页面,需要携带发票信息(invoice),方便直接编辑
  4. B页面填写后返回更新A页面的发票信息(invoice)

简单的实现是 A到B的传值使用query实现,B到A的传值使用globalData实现。

  // A页面
  onShow(){
    if(globalData.temp_invoice_info){
      this.setData({
        invoice:globalData.temp_invoice_info
      },()=>{
        globalData.temp_invoice_info=null;
      })
    }
  },
  //跳转填写发票表单信息
  toFillOutInvoiceInfo() {
    wx.navigateTo({
      url: `/pages/invoice/invoice-info-form/invoice-info-form?amount=${
        this.data.detail.amount
      }&invoice=${
        this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : ""
      }`,
    });
  },
​
 //B页面
 onLoad(options){
   this.data.options = options;
   if (this.data.options.invoice) {
     this.setData({
       invoice: JSON.parse(this.data.options.invoice)
     })
   }
 },
 submit(){
   globalData.temp_invoice_info = this.data.invoice;
   wx.navigateBack();
 }

如下EventChannel的实现

//A页面(注意这里需要使用箭头函数(访问this))
  //跳转填写发票表单信息
  toFillOutInvoiceInfo() {
    wx.navigateTo({
      url: `/pages/invoice/invoice-info-form/invoice-info-form`,
      events:{
        updateInvoice:(result)=>{
          this.setData({invoice:result})
        }
      },
      success:(res)=>{
        const params = {
          amount: this.data.amount,
          invoice:this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : ""
        }
        res.eventChannel.emit('sendQueryParams',params)
      }
    });
  },
    
  //B页面
  onLoad(){
    this.getOpenerEventChannel().once('sendQueryParams',(params)=>{
      const {amount,invoice} = parmas;
      this.setData({amount,invoice})
    })
  }
  submit(){
    this.getOpenerEventChannel().emit('updateInvoice',this.data.invoice);
    wx.navigateBack();
  }

还有一个额外的地方就是EventChannel可以在A-B-C多个页面直接建立数据通道。官方示例

//注意this.getOpenerEventChannel() 只能在navigateTo模板页面使用,其他更多页面使用时,
//可以保存在getApp()全局实例中以备其他页面使用
// 保留AB通道事件,已备C页面给A页面发送数据
// B页面
    const eventChannel = this.getOpenerEventChannel()
    getApp().pageBEventChannel = eventChannel
//C页面 
 getApp().pageBEventChannel.emit('PageAacceptDataFromPageC', { data: 'Page C->A' });

有不对或扩展的地方欢迎大家批评交流。

最后一次编辑于  2022-08-11  
点赞 14
收藏
评论

7 个评论

  • tianci
    tianci
    2022-08-08
    event:{
            updateInvoice:(result)=>{
              this.setData({invoice:result})
            }
          }
    文章挺好,但这里有个问题,event漏了个s,是events。怪不得没反应...
    
    2022-08-08
    赞同 2
    回复
  • sky
    sky
    2023-05-17

    官方有示例吗?能发链接吗?我看到的只是阐述用法。

    2023-05-17
    赞同
    回复 3
  • 在路上
    在路上
    2023-03-08

    H5中可以使用EventChannel吗


    2023-03-08
    赞同
    回复 1
    • simple
      simple
      2023-03-09
      仅小程序内支持
      2023-03-09
      回复
  • 清蒸鱼
    清蒸鱼
    2022-11-17

    ·只能在相邻页面间吗?文档这么说“如果一个页面由另一个页面通过 wx.navigateTo 打开,这两个页面间将建立一条数据通道:”

    2022-11-17
    赞同
    回复 4
    • simple
      simple
      2022-11-18
      不相邻也可以的,参见文章末尾的官方示例,可以在放在全局对象中,C页面获取该对象即可与A页面通信
      2022-11-18
      1
      回复
    • 清蒸鱼
      清蒸鱼
      2022-11-19回复simple
      好的
      2022-11-19
      回复
    • 清蒸鱼
      清蒸鱼
      2022-11-29回复simple
      卧槽,一个更坑的问题,只能在B页面中放到全局对象。我在A页面导航到B的成功回调里,设置了监听,顺便就放到全局,结果监听就不执行了...
      2022-11-29
      回复
    • simple
      simple
      2022-11-29回复清蒸鱼
      那就只在B页面中onLoad中处理就行了,以官方示例为准
      2022-11-29
      回复
  • 坏蛋
    坏蛋
    2022-09-19

    我这个怎么解决,第一次接触,完全不懂

    https://developers.weixin.qq.com/community/develop/article/doc/0004e8237c88781ee5ad26a0156c13?jumpto=comment&commentid=000c82aaf507382ff78e58c305b4

    2022-09-19
    赞同
    回复
  • 内内纯外外
    内内纯外外
    2022-09-11

    这个是不是可以完全替代Pubsub了

    2022-09-11
    赞同
    回复
  • flag
    flag
    2022-09-02

    老哥EventChannel.once的使用场景有那些啊,感觉和EventChannel.on没什么区别啊,还有EventChannel.off的第二个参数是啥啊。


    2022-09-02
    赞同
    回复 1
    • simple
      simple
      2022-09-04
      1.EventChannel.once实际是借鉴addEventListener的once参数,表示事件处理函数在添加之后最多只调用一次,但这里emit时前一个页面在navigateTo的success中使用的,大部分情况是不会重复emit事件的。假设在A页面的emit中使用setInterval或其他方式重复触发事件,在B页面的once中的函数只响应第一次触发并执行。
      2.EventChannel.off 实际是移除监听处理函数,因为同一个事件可以有多个处理函数,第二个参数就是处理函数引用名(给on或once函数的函数引用名),但我目前实际测试第二个参数是无效的。
      2022-09-04
      回复
登录 后发表内容