微信小程序页面间通信的5种方式


Posted in Javascript onMarch 31, 2017

PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的。

微信小程序页面间通信的5种方式

如上图,这是一个常见结构的小程序:首页是一个双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,通过订阅发布实现通信。在发布事件时,激活对方方法,同时传入参数,执行事件的订阅方法

微信小程序页面间通信的5种方式

/* /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属性,可能会有一些风险

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
跨浏览器开发经验总结(三)   警惕“IE依赖综合症”
May 13 Javascript
jQuery插件-jRating评分插件源码分析及使用方法
Dec 28 Javascript
AngularJS Phonecat实例讲解
Nov 21 Javascript
canvas压缩图片转换成base64格式输出文件流
Mar 09 Javascript
原生js实现密码输入框值的显示隐藏
Jul 17 Javascript
利用vscode调试编译后的js代码详解
May 14 Javascript
Vue中的情侣属性$dispatch和$broadcast详解
Mar 07 Javascript
详解JSON和JSONP劫持以及解决方法
Mar 08 Javascript
vue 中几种传值方法(3种)
Nov 12 Javascript
JS document文档的简单操作完整示例
Jan 13 Javascript
详解ES6数组方法find()、findIndex()的总结
May 12 Javascript
Element Collapse 折叠面板的使用方法
Jul 26 Javascript
ES6新特性之Symbol类型用法分析
Mar 31 #Javascript
微信小程序 后台登录(非微信账号)实例详解
Mar 31 #Javascript
Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )
Mar 31 #Javascript
ES6新特性之Object的变化分析
Mar 31 #Javascript
ES6新数据结构Set与WeakSet用法分析
Mar 31 #Javascript
ES6新数据结构Map功能与用法示例
Mar 31 #Javascript
基于AGS JS开发自定义贴图图层
Mar 31 #Javascript
You might like
php 静态变量的初始化
2009/11/15 PHP
关于php curl获取301或302转向的网址问题的解决方法
2011/06/02 PHP
Apache中php.ini的设置方法
2013/02/28 PHP
PHP统计当前在线用户数实例讲解
2015/10/21 PHP
在b/s开发中经常用到的javaScript技术
2006/08/23 Javascript
缓动函数requestAnimationFrame 更好的实现浏览器经动画
2012/12/07 Javascript
14款NodeJS Web框架推荐
2014/07/11 NodeJs
js获取域名的方法
2015/01/27 Javascript
jQuery实现高亮显示网页关键词的方法
2015/08/07 Javascript
详解AngularJS中自定义过滤器
2015/12/28 Javascript
Bootstrap table学习笔记(2) 前后端分页模糊查询
2017/05/18 Javascript
JS+php后台实现文件上传功能详解
2019/03/02 Javascript
详解JSON.stringify()的5个秘密特性
2020/05/26 Javascript
如何在JavaScript中等分数组的实现
2020/12/13 Javascript
Python FTP操作类代码分享
2014/05/13 Python
python学习之第三方包安装方法(两种方法)
2015/07/30 Python
详解Python中的Numpy、SciPy、MatPlotLib安装与配置
2017/11/17 Python
python使用Pycharm创建一个Django项目
2018/03/05 Python
python docx 中文字体设置的操作方法
2018/05/08 Python
tensorflow学习教程之文本分类详析
2018/08/07 Python
使用Python 正则匹配两个特定字符之间的字符方法
2018/12/24 Python
pandas数据处理之绘图的实现
2020/06/15 Python
Python实现弹球小游戏
2020/08/01 Python
香港优质食材和美酒专门店:FoodWise
2017/09/01 全球购物
韩国保养品、日本药妆购物网:小三美日
2018/12/30 全球购物
写一个方法1000的阶乘
2012/11/21 面试题
员工自我鉴定
2013/10/09 职场文书
优秀应届毕业生自荐书
2014/06/29 职场文书
实验心得体会
2014/09/05 职场文书
科学发展观标语
2014/10/08 职场文书
领导走群众路线整改措施思想汇报
2014/10/12 职场文书
2014年车间工作总结
2014/11/21 职场文书
店面出租协议书范本
2014/11/28 职场文书
匿名信格式范文
2015/05/27 职场文书
幼儿园科学课教学反思
2016/03/03 职场文书
Oracle11g r2 卸载干净重装的详细教程(亲测有效已重装过)
2021/06/04 Oracle