微信小程序页面间通信的5种方式
pagemodel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的。
如上图,这是一个常见结构的小程序:首页是一个双tab框架pagea和pageb,子页面pageb, pagec。
让我们假设这样一个场景:首页pagea有一个飘数,当我们从pagea新开pagec后,做一些操作,再回退到pagea的时候,这个飘数要刷新。很显然,这需要在pagec中做操作时,能通知到pagea,以便pagea做相应的联动变化。
这里的通知,专业点说就是页面通信。所谓通信,u3认为要满足下面两个条件:
- 激活对方的一个方法调用
- 能够向被激活的方法传递数据
本文将根据项目实践,结合小程序自身特点,就小程序页面间通信方式作一个探讨与小结。
通信分类
按页面层级(或展示路径)可以分为:
- 兄弟页面间通信。如多tab页面间通信,pagea,pageb之间通信
- 父路径页面向子路径页面通信,如pagea向pagec通信
- 子路径页面向父路径页面通信,如pagec向pagea通信
按通信时激活对方方法时机,又可以分为:
- 延迟激活,即我在pagec做完操作,等返回到pagea再激活pagea的方法调用
- 立即激活,即我在pagec做完操作,在pagec激活pagea的方法调用
方式一:onshow/onhide + localstorage
利用onshow/onhide激活方法,通过localstorage传递数据。大概逻辑如下
// pagea let isinitselfshow = true; page({ data: { hellomsg: 'hello from pagea' }, onshow() { // 页面初始化也会触发onshow,这种情况可能不需要检查通信 if (isinitselfshow) return; let newhello = wx.getstoragesync('__data'); if (newhello) { this.setdata({ hellomsg: newhello }); // 清队上次通信数据 wx.clearstoragesync('__data'); } }, onhide() { isinitselfshow = false; }, goc() { wx.navigateto({ url: '/pages/c/c' }); } });
// pagec page({ dosomething() { wx.setstoragesync('__data', 'hello from pagec'); } });
优点:实现简单,容易理解
缺点:如果完成通信后,没有即时清除通信数据,可能会出现问题。另外因为依赖localstorage,而localstorage可能出现读写失败,从面造成通信失败
注意点:页面初始化时也会触发onshow
方式二:onshow/onhide + 小程序globaldata
同方式一一样,利用onshow/onhide激活方法,通过读写小程序globaldata完成数据传递
// pagea let isinitselfshow = true; let app = getapp(); page({ data: { hellomsg: 'hello from pagea' }, onshow() { if (isinitselfshow) return; let newhello = app.$$data.hellomsg; if (newhello) { this.setdata({ hellomsg: newhello }); // 清队上次通信数据 app.$$data.hellomsg = null; } }, onhide() { isinitselfshow = false; }, goc() { wx.navigateto({ url: '/pages/c/c' }); } });
// pagec let app = getapp(); page({ dosomething() { app.$$data.hellomsg = 'hello from pagec'; } });
优点:实现简单,实现理解。因为不读写localstorage,直接操作内存,所以相比方式1,速度更快,更可靠
缺点:同方式1一样,要注意globaldata污染
方式三:eventbus(或者叫pubsub)方式
这种方式要先实现一个pubsub,通过订阅发布实现通信。在发布事件时,激活对方方法,同时传入参数,执行事件的订阅方法
/* /plugins/pubsub.js * 一个简单的pubsub */ export default class pubsub { constructor() { this.pubsubcache = { $uid: 0 }; } on(type, handler) { let cache = this.pubsubcache[type] || (this.pubsubcache[type] = {}); handler.$uid = handler.$uid || this.pubsubcache.$uid++; cache[handler.$uid] = handler; } emit(type, ...param) { let cache = this.pubsubcache[type], key, tmp; if(!cache) return; for(key in cache) { tmp = cache[key]; cache[key].call(this, ...param); } } off(type, handler) { let counter = 0, $type, cache = this.pubsubcache[type]; if(handler == null) { if(!cache) return true; return !!this.pubsubcache[type] && (delete this.pubsubcache[type]); } else { !!this.pubsubcache[type] && (delete this.pubsubcache[type][handler.$uid]); } for($type in cache) { counter++; } return !counter && (delete this.pubsubcache[type]); } }
//pagea let app = getapp(); page({ data: { hellomsg: 'hello from pagea' }, onload() { app.pubsub.on('hello', (number) => { this.setdata({ hellomsg: 'hello times:' + number }); }); }, goc() { wx.navigateto({ url: '/pages/c/c' }); } });
//pagec let app = getapp(); let counter = 0; page({ dosomething() { app.pubsub.emit('hello', ++counter); }, off() { app.pubsub.off('hello'); } });
缺点:要非常注意重复绑定的问题
方式四:gloabeldata watcher方式
前面提到方式中,我们有利用globaldata完成通信。现在数据绑定流行,结合redux单一store的思想,如果我们直接watch一个globaldata,那么要通信,只需修改这个data值,通过water去激活调用。同时修改的data值,本身就可以做为参数数据。
为了方便演示,这里使用oba这个开源库做为对象监控库,有兴趣的话,可以自己实现一个。
//pagea import oba from '../../plugin/oba'; let app = getapp(); page({ data: { hellomsg: 'hello from pagea' }, onload() { oba(app.$$data, (prop, newvalue, oldvalue) => { this.setdata({ hellomsg: 'hello times: ' + [prop, newvalue, oldvalue].join('#') }); }); }, goc() { wx.navigateto({ url: '/pages/c/c' }); } });
//pagec let app = getapp(); let counter = 0; page({ dosomething() { app.$$data.hellotimes = ++counter; } });
优点:数据驱动,单一数据源,便于调试
缺点:重复watch的问题还是存在,要想办法避免
方式五:通过hack方法直接调用通信页面的方法
直接缓存页面pagemodel, 通信时,直接找到要通信页面的pagemodel,进而可以访问通信页面pagemodel所有的属性,方法。简直不能太cool,感谢小组内小伙伴发现这么amazing的方式。有人肯定会问了,怎么拿到这个所有的pagemodel呢。其它很简单,每个页面有onload方法,我们在这个事件中,把this(即些页面pagemodel)缓存即可,缓存时用页面路径作key,方便查找。那么页面路径怎么获取呢,答案就是page__route__这个属性
// plugin/pages.js // 缓存pagemodel,一个简要实现 export default class pm { constructor() { this.$$cache = {}; } add(pagemodel) { let pagepath = this._getpagemodelpath(pagemodel); this.$$cache[pagepath] = pagemodel; } get(pagepath) { return this.$$cache[pagepath]; } delete(pagemodel) { try { delete this.$$cache[this._getpagemodelpath(pagemodel)]; } catch (e) { } } _getpagemodelpath(page) { // 关键点 return page.__route__; } }
// pagea let app = getapp(); page({ data: { hellomsg: 'hello from pagea' }, onload() { app.pages.add(this); }, goc() { wx.navigateto({ url: '/pages/c/c' }); }, sayhello(msg) { this.setdata({ hellomsg: msg }); } });
//pagec let app = getapp(); page({ dosomething() { // 见证奇迹的时刻 app.pages.get('pages/a/a').sayhello('hello u3xyz.com'); } });
优点:一针见血,功能强大,可以向要通信页面做你想做的任何事。无需要绑定,订阅,所以也就不存在重复的情况
缺点:使用了__route__这个hack属性,可能会有一些风险
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: DVD案例(对象数组)
下一篇: 小案例:百度搜索