微信小程序页面间通信的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 相关文章推荐
iis6+javascript Add an Extension File
Jun 13 Javascript
jquery获得页面元素的坐标值实现思路及代码
Apr 15 Javascript
file控件选择上传文件确定后触发的js事件是哪个
Mar 17 Javascript
基于jquery实现发送文章到手机的代码
Dec 26 Javascript
js中不同的height, top的区别对比
Sep 24 Javascript
JavaScript实现弹出模态窗体并接受传值的方法
Feb 12 Javascript
jQuery leonaScroll 1.1 自定义滚动条插件(推荐)
Sep 17 Javascript
JS调用某段SQL语句的方法
Oct 20 Javascript
JS常见创建类的方法小结【工厂方式,构造器方式,原型方式,联合方式等】
Apr 01 Javascript
微信公众号平台接口开发 菜单管理的实现
Aug 14 Javascript
Ant Design Pro 下实现文件下载的实现代码
Dec 03 Javascript
jquery实现拖拽添加元素功能
Dec 01 jQuery
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
解决MySQL中文输出变成问号的问题
2008/06/05 PHP
php下清空字符串中的HTML标签的代码
2010/09/06 PHP
php的memcached客户端memcached
2011/06/14 PHP
使用Curl进行抓取远程内容时url中文编码问题示例探讨
2013/10/29 PHP
php实现仿写CodeIgniter的购物车类
2015/07/29 PHP
一个不错的应用,用于提交获取文章内容,不推荐用
2007/03/03 Javascript
tbody元素支持嵌套的注意方法
2007/03/24 Javascript
jQuery中:lt选择器用法实例
2014/12/29 Javascript
JavaScript检测浏览器cookie是否已经启动的方法
2015/02/27 Javascript
jQuery控制文本框只能输入数字和字母及使用方法
2016/05/26 Javascript
详解自动生成博客目录案例
2016/12/09 Javascript
[00:06]Yes,it worked!小卡尔成功穿越时空加入战场!
2019/07/20 DOTA
使用 Python 获取 Linux 系统信息的代码
2014/07/13 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
浅析python中的迭代与迭代对象
2018/10/08 Python
Python opencv实现人眼/人脸识别以及实时打码处理
2019/04/29 Python
使用django的ORM框架按月统计近一年内的数据方法
2019/07/18 Python
python super用法及原理详解
2020/01/20 Python
Python关于反射的实例代码分享
2020/02/20 Python
如何用Matlab和Python读取Netcdf文件
2021/02/19 Python
孤独星球出版物:Lonely Planet Publications
2018/03/17 全球购物
澳大利亚最受欢迎的美发和美容在线商店:Catwalk
2018/12/12 全球购物
美国在线购买和出售礼品卡网站:EJ Gift Cards
2019/06/09 全球购物
购买正版游戏和游戏激活码:Green Man Gaming
2019/11/06 全球购物
关键字throw与throws的用法差异
2016/11/22 面试题
花店创业计划书范文
2014/02/07 职场文书
三八妇女节演讲稿
2014/05/27 职场文书
统计员岗位职责范本
2015/04/14 职场文书
关于上班时间调整的通知
2015/04/23 职场文书
员工年度工作总结2015
2015/05/18 职场文书
安全生产奖惩制度
2015/08/06 职场文书
煤矿安全生产工作总结
2015/08/13 职场文书
导游词之山东孔庙
2019/11/04 职场文书
html+css 实现简易导航栏功能
2021/04/07 HTML / CSS
利用js实现简单开关灯代码
2021/11/23 Javascript
不想升级Win11?教你彻底锁定老版Windows系统的方法(附下载地址)
2022/09/23 数码科技