出发点:公司的一个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', '我是测试消息')
}
},
})