[转] 微信小程序页面间通信的5种方式
微信小程序页面间通的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属性,可能会有一些风险
以上!
原文地址: http://www.u3xyz.com/#/detail/20
[转] 微信小程序页面间通信的5种方式的更多相关文章
- 微信小程序页面跳转 的几种方式
最近在做微信小程序,碰到页面跳转的问题,总结一下页面之间跳转的方式 一.wx.navigateTo(OBJECT) 这是最普遍的一种跳转方式,其官方解释为:“保留当前页面,跳转到应用内的某个页面” 类 ...
- 微信小程序页面跳转的三种方式总结
原文链接 https://blog.csdn.net/zgmu/article/details/72123329 首先我们了解到,小程序规定页面路径只能有五层,所以我们尽量避免多层级的页面跳转 页面跳 ...
- 在微信小程序页面间传递数据总结
在微信小程序页面间传递数据 原文链接:https://www.jianshu.com/p/dae1bac5fc75 在开发微信小程序过程之中,遇到这么一些需要在微信小程序页面之间进行数据的传递的情况, ...
- 微信小程序页面跳转的四种方法
wx.navigateTo({}) ,保留当前页面,跳转到应用内的某个页面,使用 wx.navigateBack 可以返回; 示例: 1 wx.navigateTo({ 2 url:'../test/ ...
- 微信小程序之回到顶部的两种方式
第一种:使用view标签形式回到顶部 WXML: <image src='../../img/button-top.png' class='goTop' hidden='{{!floorstat ...
- 微信小程序 - 获取用户信息的几种方式
1. 老接口(上线使用-测试用button先获取用户信息) // 登录 wx.login({ success: res => { // 发送 res.code 到后台换取 openId, ses ...
- 微信小程序页面通信
目录 微信小程序页面通信 方式一:通过URL 方式二:通过全局变量 方式三:通过本地存储 方式四:通过路由栈 微信小程序页面通信 方式一:通过URL // A 页面 wx.navigateTo({ u ...
- 微信小程序页面跳转方法总结
微信小程序页面跳转目前有以下方法(不全面的欢迎补充): 1. 利用小程序提供的 API 跳转: // 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面.// 注 ...
- 微信小程序页面调用自定义组件内的事件
微信小程序页面调用自定义组件内的事件 page page.json { "usingComponents": { "my-component": ". ...
随机推荐
- vue 点击当前元素添加class 去掉兄弟的class 获取当前点击元素的文字
点击当前标签给其添加class,兄弟标签class删除 然后获取当前点击元素的文字 演示地址: https://xibushijie.github.io/static/addClass.html &l ...
- 【ARC101F】Robots and Exits 树状数组
题目大意 有 \(n\) 个机器人和 \(m\) 个出口. 这 \(n\) 个机器人的初始位置是 \(a_1,a_2,\ldots,a_n\),这 \(m\) 个出口的位置是 \(b_1,b_2,\l ...
- [Qualcomm]A Detailed History of Qualcomm 高通的前世今生
https://www.semiwiki.com/forum/content/7353-detailed-history-qualcomm.html
- maven在windows及linux环境下安装
maven下载 下载地址:https://maven.apache.org/download.cgi maven在windows下安装 解压到D盘 修改配置文件 进入conf,打开settings.x ...
- CF1120D(神奇的构造+最小生成树)
考虑把树展开,单独把叶子节点拿出来 于是可以把控制点\(x\)的,抽象成是在它叶子节点间连权值为\(c_x\)的边 显然只用在\(x\)子树的最左边的叶子节点和最右边的叶子节点的下一个节点连边(最后一 ...
- [SDOI2006] 保安站岗
题目链接 第一遍不知道为什么就爆零了…… 第二遍改了一下策略,思路没变,结果不知道为什么就 A 了??? 树形 DP 经典问题:选择最少点以覆盖树上所有点(边). 对于本题,设 dp[i][0/1][ ...
- PR视频剪辑
PR视频剪辑 新手问题1: 将素材导入到Adobe Premiere Pro CC后为什么无法拖入到时间轴上 解决办法:没有建立有序列所致,CC不会一开始就让你新建序列,图中间处写的好清楚“无序列”. ...
- Docker:dockerfile自动构建镜像 [六]
一.手动docker镜像的缺点 相对于手动制作的docker镜像,使用dockerfile构建的镜像有以下优点: 1.dockerfile只有几kb,便于传输 2.使用dockerfile构建出来的镜 ...
- solr的域
solrhom相当于存放数据的地方,里面一个个文件相当于数据库,每个数据库里面有两个文件夹,一个data,一个conf,conf下有一个schema.xml文件,配置域,相当于表的字段. solr中内 ...
- NOI-OJ 2.2 ID:6261 汉诺塔
思路 汉诺塔是递归思想最经典的例子,通过递归不断缩小问题,将n个盘子的问题简化n-1个,直至1个. 三个盘子,分别为A:from,B:to,C:by(A为起点盘,B为目标盘,C为中转盘) 过程 将1- ...