评论

小程序页面通信、数据刷新、事件总线 、event bus 终极解决方案之 iny-bus

小程序页面通信、数据刷新、事件总线 、event bus 终极解决方案之 iny-bus

#### 背景介绍


在各种小程序中,我们经常会遇到 这种情况

有一个 列表,点击列表中的一项进入详情,详情有个按钮,删除了这一项,这个时候当用户返回到列表页时,

发现列表中的这一项依然存在,这种情况,就是一个 `bug`,也就是数据不同步问题,这个时候测试小姐姐

肯定会找你,让你解决,这个时候,你也许会很快速的解决,但过一会儿,测试小姐姐又来找你说,我打开了

四五个页面更改了用户状态,但我一层一层返回到首页,发现有好几个页面数据没有刷新,也是一个 bug,

这个时候你就犯愁了,怎么解决,常规方法有下面几种

#### 解决方法

    1. 将所有请求放到 生命周期 `onShow` 中,只要我们页面重新显示,就会重新请求,数据也会刷新

    2. 通过用 `getCurrentPages` 获取页面栈,然后找到对应的 页面实例,调用实例方法,去刷新数据

    3. 通过设置一个全局变量,例如 App.globalData.xxx,通过改变这个变量的值,然后在对应 onShow

       中检查,如果值已改变,刷新数据

    4. 在打开详情页时,使用 redirectTo 而不是 navigateTo,这样在打开新的页面时,会销毁当前页面,

       返回时就不会回到这个里面,自然也不会有数据不同步问题

  

#### 存在的问题


1. 假如我们将 所有 请求放到 onShow 生命周期中,自然能解决所有数据刷新问题,但是 onShow

这个生命周期,有两个问题

第一个问题,它其实是在 onLoad 后面执行的,也就是说,假如请求耗时相同,从它发起请求到页面渲染,

会比 onLoad 慢

第二个问题,那就是页面隐藏、调用微信分享、锁频等等都会触发执行,请求放置于 `onShow` 中就会造成

大量不需要的请求,造成服务器压力,多余的资源浪费、也会造成用户体验不好的问题


2. 通过 `getCurrentPages` 获取页面栈,然后找到对应的 页面实例,调用实例方法,去刷新数据,这也

不失为一个办法,但是就如微信官方文档所说

> 不要尝试修改页面栈,会导致路由以及页面状态错误。

> 不要在 App.onLaunch 的时候调用 `getCurrentPages()`,此时 page 还没有生成。


同时、当需要通信的页面有两个、三个、多个呢,这里去使用 `getCurrentPages` 就会比较困难、繁琐


3. 通过设置全局变量的方法,当需要使用的地方比较少时,可以接受,当使用的地方多的时候,维护起来

就会很困难,代码过于臃肿,也会有很多问题


4. 使用 redirectTo 而不是 navigateTo,从用来体验来说,很糟糕,并且只存在一个页面,对于

tab 页面,它也无能为力,不推荐使用


#### 最佳实践


在 Vue 中, 可以通过 new Vue() 来实现一个 event bus作为事件总线,来达到事件通知的功能,在各大

框架中,也有自身的事件机制实现,那么我们完全可以通过同样的方法,实现一个事件中心,来管理我们的事件,

同时,解决我们的问题。iny-bus 就是这样一个及其轻量的事件库,使用 typescript 编写,100% 测试覆

盖率,能运行 js 的环境,就能使用



传送门

[源码](https://github.com/landluck/iny-bus)

[NPM](https://www.npmjs.com/package/iny-bus)

[文档](https://landluck.github.io/iny-bus/docs/)



#### 简单使用


iny-bus 使用及其简单,在需要的页面 onLoad 中添加事件监听, 在需要触发事件的地方派发事件,使监

听该事件的每个页面执行处理函数,达到通信和刷新数据的目的,在小程序中的使用可以参考以下代码

   // 小程序
 
   import bus from 'iny-bus'
 
 
 
   // 添加事件监听
 
   // 在 onLoad 中注册, 避免在 onShow 中使用
 
    onLoad () {
 
      this.eventId = bus.on('事件名', (a, b, c, d) => {
 
      // 支持多参数
 
      console.log(a, b, c, d)
 
 
 
      this.setData({ a })
 
      // 调用页面请求函数,刷新数据
 
      this.refreshPageData()
 
    })
 
 
 
    // 添加只需要执行一次的 事件监听
 
 
 
    this.eventIdOnce = bus.once('事件名', () => {
 
      // do some thing
 
     })
 
   }
 
 
 
   // 移除事件监听,该函数有两个参数,第二个事件id不传,会移除整个事件监听,传入ID,会移除该
 
   // 页面的事件监听,避免多余资源浪费, 在添加事件监/// 听后,页面卸载(onUnload)时建议移除
 
 
 
   onUnload () {
 
     bus.remove('事件名', this.eventId)
 
   }
 
 
 
   // 派发事件,触发事件监听处更新视图
 
  // 支持多参传递
 
  onClick () {
 
    bus.emit('事件名', a, b, c)
 
  }



更详细的使用和例子可以参考 [Github iny-bus 小程序代码](https://github.com/landluck/iny-bus/tree/master/examples)




#### iny-bus 具体实现

* 基本打包工具,这里使用非常优秀的开源库 [typescript-library-starter](https://github.com/alexjoverm/typescript-library-starter),具体细节不展开


* 测试工具 使用 facebook 的 [jest](https://github.com/facebook/jest)


* build ci 使用 [travis-ci](https://www.travis-ci.org/


* 测试覆盖率上传使用 [codecov](https://codecov.io/)


* 具体的其他细节大家可以看源码中的 [package.json](https://github.com/landluck/iny-bus/blob/master/package.json),这里就一一展开讲了

iny-bus 的核心代码,其实就这么多,总的来说,非常少,但是能解决我们在小程序中遇到的大量 通信 和 数据刷新问题,是采用 各大平台小程序 原生开发时,页面通信的不二之选,同时,100% 的测试覆盖率,确保了 iny-bus 在使用中的稳定性和安全性,当然,每个库都是从简单走向复杂,功能慢慢完善,如果

大家在使用或者源码中发现了bug或者可以优化的点,欢迎大家提 pr 或者直接联系我


最后,如果 iny-bus 给你提供了帮助或者让你有任何收获,请给 作者 点个赞,感谢大家 [点赞](https://github.com/landluck/iny-bus)






最后一次编辑于  08-04  (未经腾讯允许,不得转载)
点赞 5
收藏
评论

6 个评论

  • 刘汝佳
    刘汝佳
    08-06

    我的是进入详情页传了index,当他删除时 缓存index,还有他的状态,然后跳转到上一页,上一页的onShow里面获取缓存,获取成功后,删除缓存,下标index的数据清空,并且wx:for中的数据 为空时不显示,

    08-06
    赞同 1
    回复 1
    • landluck
      landluck
      08-06
      简单是可以这么做,更为复杂的业务场景,这样去做就会慢慢的混乱起来,像我们公司小程序 50+ 页面,五六个开发人员,相对而言 bus 会是更好的选择
      08-06
      回复
  • 陈式坚
    陈式坚
    08-05

    感谢分享,目前还是用第二种解决方案

    文章提到的问题

    不要尝试修改页面栈,会导致路由以及页面状态错误。

    不要在 App.onLaunch 的时候调用 `getCurrentPages()`,此时 page 还没有生成。

    其实不会发生,因为我们调用的页面栈中的方法而不是直接修改,所以不会有问题

    其次,既然是页面通信,肯定不会存在App.onLaunch去调用的场景

    多个页面栈的情况的,的确不好解决,我们的解决方法是框架一开始就通过页面name(自己设置一个以表明是同个页面)记录下来。后面只要循环一下就可以

    ------

    eventBus有好优化,坏处也是不好管理,监听多那个乱得太夸张

    ----

    eventBus的我们一般用的是mitt(https://github.com/developit/mitt

    ---

    第二种解决方案目前觉得还是最好的,好管理



    08-05
    赞同 1
    回复 2
    • landluck
      landluck
      08-06

      我是不太建议使用 getCurrentPages 的,原因如下

      使用 getCurrentPages,当涉及多个页面,如果使用 name 去循环的话,需要每一个页面提供一个相同的处理函数,如何不同,判断逻辑就有点冗余

        同时,开发人员在开发就非常需要去约定相关的内容,需要关心每一个需要刷新的页面,逻辑就耦合了     

      使用 bus 的话,就不需要关心其他页面的内容,只需要 当前 页面 触发 事件,其他页面监听处理即可,在复用方面也非常简单

      而且, getCurrentPages 是无法在同一个页面中的不同组件中使用的,典型的 兄弟组件如何通信,而 bus 是完全能够使用的,这也是 bus 的一大优势

       


      08-06
      回复
    • 陈式坚
      陈式坚
      08-06回复landluck

      em..

      页面提供函数这个本来就该怎么做?

      兄弟组件通信肯定也是用triggerEvent和selectComp去处理,这个应该也是和前端框架相同的...

      eventBus和提供函数这种方法刚好是反过来的思维,eventBus看似取巧但是实际合作很麻烦,所以我们淘汰了这种方案

      其实,小程序自己也搞了eventBus,但是在路由里把eventBus传过去(处理上和提供函数思路是一样的,类似于扩展而不是拿来通信)



      08-06
      1
      回复
  • 穿着裤衩到处跑
    穿着裤衩到处跑
    09-18

    感谢楼主分享,学习了

    09-18
    赞同
    回复
  • 微云
    微云
    08-21

    支持,用上了!

    08-21
    赞同
    回复
  • 冰是沉默的水
    冰是沉默的水
    08-13

    在从列表到详情页面进行操作后返回到列表页面,我觉得currentPage这中方式挺好的,闭环小,不涉及到其他页面。耦合低。

    08-13
    赞同
    回复 5
    • landluck
      landluck
      08-14

      那比如说多个页面需要刷新数据呢,也不只是一个页面,如果涉及webview的H5刷新呢

      08-14
      1
      回复
    • 冰是沉默的水
      冰是沉默的水
      08-14回复landluck
      先说小程序进入到webview上的,因为全局变量缓存这些都不能互通,所以我在src链接上拼接了参数,如果参数太多的和链接超过255的话那就得和后端去做一次请求交互,从webview往小程序也是一样,要么在网页上把操作结果提交到服务器,要么就在跳转小程序时把参数拼接上,还有一个叫postmessage的没用过,不太了解
      08-14
      回复
    • 冰是沉默的水
      冰是沉默的水
      08-14回复landluck
      小程序内的多个页面的数据同步我个人喜欢用缓存,因为可以在storage里看到,用全局变量也行,用发布订阅模式实时改变也行,但是多人共同开发我没经验,我觉得大家保持用一种就行,不然都用了维护起来就乐了
      08-14
      回复
    • landluck
      landluck
      08-14回复冰是沉默的水

      多人开发,尤其是技术水平不一的时候,是需要一个简单成熟的方案供大家使用的,使用 storage 和 全局变量会因为每个人的习惯和别的因素的不同导致在业务复杂的时候及其混乱的,所以就需要将这些公用模块作为一个类似插件的功能,去供大家使用,当功能不够时,有专门的人维护插件,而不是每个人都可以随便改,这样有利于项目的稳定、开发效率高、而不仅限于考虑功能的实现

      08-14
      回复
    • landluck
      landluck
      08-14回复冰是沉默的水

      将事件通信、数据刷新的解决方案做成 npm 包,独立发布、不同的小程序可以使用 npm 包解决同样的痛点,就不会出现在出现同样的功能时,需要cv代码

      08-14
      回复
  • 小面包🍞
    小面包🍞
    08-13

    这么麻烦干嘛 进入详情的时候传递列表页的下标 然后在详情页面操作的时候 去获取前一个页面的列表data 然后根据下标去修改不就可以了

    08-13
    赞同
    回复 2
    • landluck
      landluck
      08-14
      这只是其中一个场景、真正复杂的业务比这复杂多了,原则上在不建议修改在当前页面直接通过页面去修改其他页面的数据,这会造成数据数据不统一,就像不要通过 this.data.xxx = xxx 去修改数据一样
      08-14
      回复
    • 小面包🍞
      小面包🍞
      08-27回复landluck
      08-27
      回复