通过发布订阅实现小程序组件通信

765 阅读1分钟

出发点:公司的一个h5项目要改成小程序,vue写的h5实现组件和页面通信很简单,但小程序却没有提供这种跨组件或者页面通信的功能。但所谓的通信,其实就是一套发布订阅的流程,那么按照这个思路来实现它看看。

开始编写

新建一个event.js

class Event {
  constructor() {
    this.objEvents = new Map()
  }
}
const event = new Event()
export default event

订阅

// ctx上下文(就像页面里使用的this)
on(eventName, fn, ctx) {
  if (typeof fn !== "function") {
    console.error("fn must be function")
    return
  }
  // 如果订阅的事件不存在,那么就创建一个【事件名:[订阅事件的函数]】
  if (!this.objEvents.get(eventName)) {
    this.objEvents.set(eventName, [])
  }
  let value = this.objEvents.get(eventName)
  // 这个地方比较重要,为了避免重复调用某监听函数,采用先剔除原来的fn,然后再用新的fn替换
  // 调用场景如下,那么最后会删除fn,后面的fn1会被记录下来
  `on(name, fn, ctx)
   on(name, fn1, ctx)
  `
  value = value.filter(function(e) {
    return e.ctx !== ctx
  })
  value.push({'cb': fn, 'ctx': ctx})
  this.objEvents.set(eventName, value)
}

发布

emit(eventName, params) {
  const eventArr = this.objEvents.get(eventName) || []
  const args = [].slice.call(arguments, 1) || []
  if (eventArr && eventArr.length) {
    for (var i = 0, len = eventArr.length; i < len; i++) {
      eventArr[i].cb.apply(eventArr[i].ctx, args)
    }
  }
}

删除

off(eventName, fn) {
  //删除全部事件
  if (!arguments.length) {
    this.objEvents.clear()
    return
  }
  // 删除的事件不存在,则返回
  if (!this.objEvents.has(eventName)) {
    return
  }
  // 删除该类事件监听下的所有函数
  if (arguments.length === 1) {
    this.objEvents.delete(eventName)
    return
  }
  // 删除某个具体事件下的具体函数
  const eventArr = this.objEvents[eventName]
  for (var i = 0, len = eventArr.length; i < len; i++) {
    if (fn && fn === eventArr[i].cb) {
      this.objEvents[eventName].splice(fn, 1)
      break
    }
  }
}

如何使用

1、在app.js引入注册

import event from "./lib/event"
App({  
  event,  
  onLaunch: function () {}
})

2、页面或组件中订阅事件

const app = getApp()
Component({  
  data: { 
  	content: ""
  },  
  ...
  ...
  pageLifetimes: {
    show() {
      app.event.on('test', function(e) {
        this.setData({
          content: e
        })
      }, this)
    }
  },
})

3、在其它组件或页面发布事件

const app = getApp()
Component({   
  ...
  ...
  pageLifetimes: {
    show() {
      app.event.emit('test', '我是测试消息')
    }
  },
})