![a6c1aaf43386325597cda6e469ba9c50.png](https://i-blog.csdnimg.cn/blog_migrate/d2455f5124cf84daa67916379d10cf3d.jpeg)
我们先想想这样很常见的应用场景:你正在浏览自己的一篇博客,发现有瑕疵,就点击编辑按钮打开了一个新页面,确认更新后当前页面被关闭,然后博客展示页上的内容也自动更新了,那么问题来了:怎么告诉博客展示页,这篇博客有更新,需要刷新数据呢?
暴力不推荐的方案
在Page()实例中,有个生命周期回调函数:onShow(),每当页面展示在最上层时,总会执行此方法。那么我们就可以把获取博客内容的操作放在这里:
Page({ data: { blog: null } onShow() { fetch('path/to/blog?id=1').then(blog => this.setData({blog: blog})); }});
所以当编辑页面被关闭后,会触发调用博客展示页的onShow(),实现数据的重新获取。但是假设我们误点了编辑按钮,啥也没干又返回了,会有什么问题?问题倒是没有,但是会再次发起一次不必要的数据请求,不是吗?因为我们并没有更新博客,造成了资源浪费。我们需要有更好的解决方案:确切的知道博客被编辑了这个事件,然后再去做数据请求,也就是所谓的按需加载。
消息订阅机制
先来看代码吧:
//app.jsApp({ data: { subscribes: { 'BLOG_UPDATED': [], // 博客有更新 'ANOTHER_CHANGED': [], // 其他某某有变化 } }, /** * 消息订阅 * @param string subject 消息主题 * @param function callback 处理函数 * @return int 订阅句柄 */ subscribe(subject, callback) { if (undefined === this.data.subscribes[subject]) return; this.data.subscribes[subject].push(callback); return this.data.subscribes[subject].length -1; }, /** * 消息发布 * @param string subject 消息主题 */ publish(subject) { if (undefined === this.data.subscribes[subject]) return; this.data.subscribes[subject].forEach(callback => 'function' == typeof callback && callback()); } /** * 取消订阅 * @param string $subject 消息主题 * @param int flag 订阅句柄 * @param return void */ unsubscribe(subject, flag) { if(this.data.subscribes[subject] && this.data.subscribes[subject][$flag]) { this.data.subscribes[subject][$flag] = null; } }});
我们在app.js中定义了订阅事件列表subscribes和了三个方法:
- subscribe(subject, callback)
- publish(subject)
- unsubscribe(subject, flag)
首先介绍一下subscribes的格式:subscribes被定义成一个包含一系列键值对的对象,每个键代表我们的业务更新事件,值代表订阅者的事件处理方法列表。
subscribe(subject, callback)
消息订阅函数,需提供要订阅的主题和回调方法。我们还是以博客为例,假设博客详情页路径为:blog/detail(以pages目录为基准),博客编辑页为:blog/edit。在blog/detail的脚本文件中,加入以下代码:
Page({ data: { flag: null }, onReady() { let flag = getApp().subscribe('BLOG_UPDATED', () => { // 重新获取博客数据,例如: // fetch('path/to/blog?id=1').then(blog => this.setData({blog: blog})); }); this.setData({flag: flag}); }})
我们在onLoad()方法也即页面加载的时候加入订阅处理事件,并保存回调处理标识,后续需要用到。这样我们就完成了对博客更新动作的监听。接下来我们需要用publish函数对更新操作发起广播通知。
publish(subject)
在blog/edit脚本文件中,我们应该在编辑完成后调用此函数发布主题变更通知,交由app.js处理订阅方法列表:
Page({ update() { fetch('path/to/blog/update?id=1').then(() => { getApp().publish('BLOG_UPDATED'); }); }})
因为还要考虑到页面被销毁的情况,当页面不存在时,如果其他页面仍然触发了订阅事件,app.js在执行订阅处理方法列表时势必会出错,所以我们需要在订阅的同时也要明确指定什么时候取消订阅。我们在blog/detail脚本中再增加以下代码:
unload() { getApp().unsubscribe('BLOG_UPDATED', this.data.flag);}
这样当blog/detail页面被卸载后,其订阅的处理方法也随之被取消。
总结
在使用这套消息订阅发布机制后,页面间的无感更新体验会变得非常棒,通常你还在当前页面做了更新,由于页面回退和关闭会有一段很小的动画效果,此时其他页面的数据更新已经完成了,当被层叠的页面再次出现时,数据已然是最新的了。
给你代码往期回顾:
给你代码:leetcode随笔
暴走状态!小程序用户UnionID的获取及登录状态维护——给你代码
给你代码:leetcode题目加小技巧