赞
踩
移动开发中页面之间通讯是很常见的场景,比如某个页面完成操作后需要通知其他的页面刷新等等之类的。然而微信小程序(后面统称小程序)原生并未提供跨页面通讯的API,所以我们只能自己实现这样一个类似的API。那我们来试想下这个API大概要有一些什么功能?
首先我们先看下大致的架构图
系统中所有的订阅的消息通过一个全局的字典(Map)来存储,其中的key是事件标识,每个key对应一个数组(这里用数组而不用单个对象是为了能在不同的页面能用相同的key订阅事件,因为有时候一个页面发布消息需要多个页面响应),数组中每个元素是一个对象,其中target表示订阅消息的发起者,callback表示对应发起者的回调函数。然后发布消息的时候直接通过对应的key来拿到消息队列,然后遍历队列发布消息。
由于主要是逻辑实现,没有页面,所以我们新建一个js文件,我这里的目录是和pages同级目录新建lib文件夹,然后lib文件夹新建eventbus.js文件,如下图:
有个全局的字典对象,然后设计三个对外暴露API,分别是:
//eventbus.js var events = new Map() /** * 消息订阅 * key:消息标识 * target:消息发起者,用来区分相同key不同的消息 * callback:回调函数 */ function sub(key, target, callback) { } /** * 消息发布 * key:消息标识 * data:回调数据 */ function pub(key, data) { } /** * 取消订阅 * key:消息标识 * target:消息发起者,用来区分相同key不同的消息 */ function cancel(key,target) { } module.exports = { sub: sub, pub: pub, cancel: cancel }
订阅消息是事件发生的第一环,所以我们首先来写这个API。
按照之前发的架构图来看,订阅消息的时候每个key对应一个消息队列,如果消息队列中有存在target相同的消息,则直接覆盖原来的订阅内容,没有的话则将消息插入队列。
/** * 消息订阅 * key:消息标识 * target:消息发起者,用来区分相同key不同的消息 * callback:回调函数 */ function sub(key, target, callback) { //消息对象 var eobj = {'target':target,'callback':callback} //先通过key拿到对应的消息队列 var value = events.get(key) //当前key已存在消息队列说明是不同页面相同的key的消息订阅 if (Array.isArray(value)){ //过滤出消息发起者不同的消息,相当于覆盖key和target都一样的消息 value = value.filter(function(e){ return e.target != target }) //过滤出的队列重新插入此次订阅的消息 value.push(eobj) events.set(key,value) }else {//不是队列表示字典中没有包含当前key的消息,直接插入 events.set(key,[eobj]) } console.log('function sub ', events) }
订阅消息之后接下来就是发布消息并响应。
这个比较简单,也好理解。通过key来拿到字典(Map)中的消息队列,然后遍历队列逐一进行函数回调即可。
/** * 消息发布 * key:消息标识 * data:回调数据 */ function pub(key, data) { //通过key拿到消息队列 var value = events.get(key) //如果队列存在则遍历队列,然后调用消息发起者的回调函数,并将data数据进行回调 if (Array.isArray(value)){ value.map(function(e){ var target = e.target var callback = e.callback callback.call(target, data) }) } }
因为字典中存储的消息队列中包含target对象,这个对象包含的数据较大,如果再订阅消息的页面卸载(回调onupload函数)的时候不取消订阅,容易造成内存溢出。
/** * 取消订阅 * key:消息标识 * target:消息发起者,用来区分相同key不同的消息 */ function cancel(key,target) { var haskey = events.has(key) //是否存在此消息队列 if(haskey){ var value = events.get(key) if (Array.isArray(value)) { //如果队列中只有一条数据直接删除 if(value.length == 1){ events.delete(key) }else{ //如果队列中存在多条数据则过滤出和当前取消订阅target不同的消息然后重新设置到key的消息队列中 value = value.filter(function (e) { return e.target != target }) events.set(key, value) } } } console.log('function cancel ',events) }
上面写完了API,接下来就是实战了,我们先来一个简单的。
页面A跳转页面B然后在页面B中使用我们的eventbus。
//引入js文件 var event = require('../../lib/eventbus.js') var that Page({ /** * 页面的初始数据 */ data: { content: 'go to second page' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { that = this event.sub('home', that,function(content){ that.setData({ content: content }) }) }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { event.cancel('home',that) } })
<navigator url="../fun/fun">{{content}}</navigator>
var event = require('../../lib/eventbus.js') var that Page({ /** * 页面的初始数据 */ data: { content: 'do event' }, tap() { event.pub('home', 'this is new conent') wx.navigateBack({ detla: -1 }) } })
<text bindtap="tap">{{content}}</text>
我们刚进入第一个页面的时候订阅了key为home的消息,接下来看下打印:
接下来我们再看下整个流程效果图:
通过比对代码,发现结果符合我们预期的。
页面A不变,页面B做些许改变
var event = require('../../lib/eventbus.js') var that Page({ /** * 页面的初始数据 */ data: { content: 'do event' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { that = this event.sub('home', that, function (content) { that.setData({ content: content }) }) }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { event.cancel('home', that) }, tap() { event.pub('home', 'this is new conent') wx.navigateBack({ detla: -1 }) } })
<text bindtap="tap">{{content}}</text>
<navigator url="../fun1/fun1" hidden="true">go to thrid page</navigator>
然后增加第三个页面C
var event = require('../../lib/eventbus.js')
var that
Page({
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
that = this
},
tap() {
event.pub('home', 'this is new conent')
}
})
<view bindtap="tap">do event </view>
现在key为home的消息在不同页面订阅了两次,看打印先:
可以看到刚进入页面key为home的消息队列为1,后面跳转第二个页面队列为2,退出第二个页面,队列长度又变成1.
接下来我们再看下整个流程效果图:
通过比对代码,效果同样符合预期。
在和pages同级目录下新建component目录,然后在component中新建组件component1
// component/component1/component1.js var that var event = require('../../lib/eventbus.js') Component({ /** * 组件的初始数据 */ data: { content: 'component1' }, // 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容 attached: function () { console.log('attached') that = this event.sub('component', that, function (content) { that.setData({ content: content }) }) }, detached: function () { console.log('detached') event.cancel('component', that) } })
<view>{{content}}</view>
然后在页面三中引用组件component1
{
"usingComponents": {
"component1":"../../component/component1/component1"
}
}
省略...
tap() {
event.pub('component', 'this is new conent')
}
省略...
接下来看效果图:
通过比对代码,符合预期效果。
还有组件间的通讯由于篇幅有限就不做演示了,跟之前提到的方式都大同小异,有兴趣的可以自己试试。
使用姿势如下:
//这里的路径视实际情况而定,按照文中的我的写法的可以按下面的方式引用
var event = require('../../lib/eventbus.js')
event.sub(key, that, function (data) {})
event.pub(key, data)
//页面卸载了记得取消消息订阅防止内存溢出
event.cancel(key, that)
文中已经做了详细的注释和演示,如果还是有不太清楚的小伙伴欢迎留言。
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。