微信小程序页面间通信的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 相关文章推荐
Javascript下的keyCode键码值表
Apr 10 Javascript
html数组字符串拼接的最快方法
Sep 16 Javascript
Javascript 页面模板化很多人没有使用过的方法
Jun 05 Javascript
兼容最新firefox、chrome和IE的javascript图片预览实现代码
Aug 08 Javascript
JS中append字符串包含onclick无效传递参数失败的解决方案
Dec 26 Javascript
关于js中的鼠标事件总结
Jul 11 Javascript
Vue瀑布流插件的使用示例
Sep 19 Javascript
微信小程序外卖选购页实现切换分类与数量加减功能案例
Jan 15 Javascript
JS获取动态添加元素的方法详解
Jul 31 Javascript
Vue 嵌套路由使用总结(推荐)
Jan 13 Javascript
JavaScript实现沿五角星形线摆动的小圆实例详解
Jul 28 Javascript
vue实现动态给id赋值,点击事件获取当前点击的元素的id操作
Nov 09 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中实现进程间通讯
2006/10/09 PHP
php与php MySQL 之间的关系
2009/07/17 PHP
ThinkPHP自定义函数解决模板标签加减运算的方法
2015/07/03 PHP
SESSION存放在数据库用法实例
2015/08/08 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
用Javascript读取中文COOKIE的解决办法
2007/02/15 Javascript
js实现适用于素材网站的黑色多级菜单导航条效果
2015/08/24 Javascript
jquery模拟实现鼠标指针停止运动事件
2016/01/12 Javascript
仅30行代码实现Javascript中的MVC
2016/02/15 Javascript
node.js插件nodeclipse安装图文教程
2020/10/19 Javascript
把普通对象转换成json格式的对象的简单实例
2016/07/04 Javascript
浅析JavaScript中的array数组类型系统
2016/07/18 Javascript
详解Angular4中路由Router类的跳转navigate
2017/06/09 Javascript
深入浅析JS中的严格模式
2018/06/04 Javascript
JS函数进阶之继承用法实例分析
2020/01/15 Javascript
Python入门篇之正则表达式
2014/10/20 Python
使用Python保存网页上的图片或者保存页面为截图
2016/03/05 Python
python实现按任意键继续执行程序
2016/12/30 Python
Python用threading实现多线程详解
2017/02/03 Python
Python开发SQLite3数据库相关操作详解【连接,查询,插入,更新,删除,关闭等】
2017/07/27 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
2019/06/24 Python
详解python uiautomator2 watcher的使用方法
2019/09/09 Python
Django实现网页分页功能
2019/10/31 Python
解决Python3下map函数的显示问题
2019/12/04 Python
用python批量移动文件
2021/01/14 Python
Python爬虫+tkinter界面实现历史天气查询的思路详解
2021/02/22 Python
超酷炫 CSS3垂直手风琴菜单
2016/06/28 HTML / CSS
CSS3轻松实现清新 Loading 效果的简单实例
2016/06/06 HTML / CSS
美国户外服装和装备购物网站:Outland USA
2020/03/22 全球购物
华为智利官方商店:Huawei Chile
2020/05/09 全球购物
会计试用期自我评价
2014/09/19 职场文书
2015年管理人员工作总结
2015/05/13 职场文书
忠诚与背叛观后感
2015/06/04 职场文书
React Hook用法示例详解(6个常见hook)
2021/04/28 Javascript
centos8安装nginx1.9.1的详细过程
2021/08/02 Servers
使用Python获取字典键对应值的方法
2022/04/26 Python