微信小程序里实现页面间通信的有效方式探究

why 94 2024-08-21

这篇文章主要介绍了微信小程序页面间通信的5种方式,内容挺不错的,现在分享给大家,也给大家做个参考。

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

image.png

如上图,这是一个常见结构的小程序:首页是一个双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传递数据。大概逻辑如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// 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'

  });

 }

});

1

2

3

4

5

6

// pageC

Page({

 doSomething() {

  wx.setStorageSync('__data', 'hello from PageC');

 }

});

优点:实现简单,容易理解

缺点:如果完成通信后,没有即时清除通信数据,可能会出现问题。另外因为依赖localStorage,而localStorage可能出现读写失败,从面造成通信失败

注意点:页面初始化时也会触发onShow

方式二:onShow/onHide + 小程序globalData

同方式一一样,利用onShow/onHide激活方法,通过读写小程序globalData完成数据传递

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// 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'

  });

 }

});

1

2

3

4

5

6

7

8

// PageC

let app = getApp();

 

Page({

 doSomething() {

  app.$$data.helloMsg = 'hello from pageC';

 }

});

优点:实现简单,实现理解。因为不读写localStorage,直接操作内存,所以相比方式1,速度更快,更可靠

缺点:同方式1一样,要注意globalData污染

方式三:eventBus(或者叫PubSub)方式

这种方式要先实现一个PubSub,通过订阅发布实现通信。在发布事件时,激活对方方法,同时传入参数,执行事件的订阅方法

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

/* /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]);

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//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'

  });

 }

});

1

2

3

4

5

6

7

8

9

10

11

12

13

//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这个开源库做为对象监控库,有兴趣的话,可以自己实现一个。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

//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'

  });

 }

});

1

2

3

4

5

6

7

8

9

//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__这个属性

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

// 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__;

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

// 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

  });

 }

});

1

2

3

4

5

6

7

8

9

10

//pageC

 

let app = getApp();

 

Page({

 doSomething() {

  // 见证奇迹的时刻

  app.pages.get('pages/a/a').sayHello('hello u3xyz.com');

 }

});

优点:一针见血,功能强大,可以向要通信页面做你想做的任何事。无需要绑定,订阅,所以也就不存在重复的情况

缺点:使用了__route__这个hack属性,可能会有一些风险

以上就是本文的全部内容,希望对大家的学习有所帮助。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:移动应用开发架构的重要性是什么?
下一篇:概述微服务中间件
相关文章

 发表评论

暂时没有评论,来抢沙发吧~