⼩程序页⾯间通信的5种⽅式
PageModel(页⾯模型)对⼩程序⽽⾔是很重要的⼀个概念,从app.json中也可以看到,⼩程序就是由⼀个个页⾯组成的。
如上图,这是⼀个常见结构的⼩程序:⾸页是⼀个双Tab框架PageA和PageB,⼦页⾯pageB, PageC。
让我们假设这样⼀个场景:⾸页PageA有⼀个飘数,当我们从PageA新开PageC后,做⼀些操作,再回退到PageA的时候,这个飘数要刷新。很显然,这需要在PageC中做操作时,能通知到PageA,以便PageA做相应的联动变化。
这⾥的通知,专业点说就是页⾯通信。所谓通信,u3认为要满⾜下⾯两个条件:
1. 激活对⽅的⼀个⽅法调⽤
2. 能够向被激活的⽅法传递数据
本⽂将根据项⽬实践,结合⼩程序⾃⾝特点,就⼩程序页⾯间通信⽅式作⼀个探讨与⼩结。
通信分类
按页⾯层级(或展⽰路径)可以分为:
1. 兄弟页⾯间通信。如多Tab页⾯间通信,PageA,PageB之间通信
2. ⽗路径页⾯向⼦路径页⾯通信,如PageA向PageC通信
3. ⼦路径页⾯向⽗路径页⾯通信,如PageC向PageA通信
按通信时激活对⽅⽅法时机,⼜可以分为:
1. 延迟激活,即我在PageC做完操作,等返回到PageA再激活PageA的⽅法调⽤
2. ⽴即激活,即我在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() {
('hello', (number) => {
this.setData({
helloMsg: 'hello times:' + number
});
});
},
goC() {
wx.navigateTo({
url: '/pages/c/c'
});
}
});
//pageC免费小程序制作
let app = getApp();
let counter = 0;
Page({
doSomething() {
it('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) {
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。