- 微信小程序页面之间正向传值和逆向传值的方法
微信小程序页面之间正向传值和逆向传值 正向传值 一 直接使用URL传值 [代码] wx.navigateTo({ url: `/pages/contacts-edit/contacts-edit?name=zhangsan&idx=1`, }) [代码] 但是如果一个对象结构比较复杂, 数据量比较大, 即使转换成JSON也有可能会被莫名其妙的截取. 所以使用URL传值的时候, 需要先编码 我是这样做的 [代码]// A页面触发事件, 跳转到B页面 _onClickCell: function (e) { let contacts = { name: '张三', phone: '13800001111', safePhone: '138****1111', idCard: '230524202113324455', safeIdCard: '230524********4455', typeStr: '成人', gender: '0', genderStr: '保密' } // 先对数据进行JSON let jsonStr = JSON.stringify(contacts) // 对数据进行URI编码, 如果不进行这一步操作, 数据有可能会被截断, 少量数据没有问题, 如果是一个大的对象, 就容易被截断获取不到完整的数据 let data = encodeURIComponent(jsonStr) wx.navigateTo({ url: `/pages/contacts-edit/contacts-edit?contacts=${data}&idx=${idx}`, }) }, // B页面再onLoad方法中接收参数 onLoad: function (options) { let idx = (!!options.idx) ? Number(options.idx) : -1 let contacts = {} if (!!options.contacts) { let jsonStr = decodeURIComponent(options.contacts) contacts = JSON.parse(jsonStr) } this.setData({ contacts, idx }) }, [代码] 二 使用eventChannel来传递 [代码]//A页面准备跳转到B页面 _onClickCell: function (e) { let address = { id: 457, name: '小艾-3', countryCode: '86', phone: '13892292222', reginoCode: '871', city: '市辖区', area: '海淀区', street: '东北旺路8号院中关村软件园8号楼华夏科技大厦', address: '中国北京市市辖区海淀区东北旺路8号院中关村软件园8号楼华夏科技大厦' }, wx.navigateTo({ url: '/pages/address-edit/address-edit', success: res => { // 这里给要打开的页面传递数据. 第一个参数:方法key, 第二个参数:需要传递的数据 res.eventChannel.emit('setAddressEditData', address) } }) } //B页面在onLoad方法中接收参数 onLoad: function (options) { // 接收上个页面传递来的数据 let eventChannel = this.getOpenerEventChannel() // setAddressEditData和上个页面设置的相同即可 eventChannel.on('setAddressEditData', (address) => { this.setData({ address: address || {}, }) }) }, [代码] 逆向传值 一 使用全局对象, 获取全部页面来逆向传值 [代码] _onClickComplete: function () { // 获取当前全部的页面栈 let arr = getCurrentPages() // 获取到要逆向传值的上一个页面 let lastPage = (arr.length >= 2) ? arr[arr.length - 2] : undefined // 判断拿到的上一个页面是不是我们要的页面 if (!!lastPage && lastPage.route == 'pages/contacts-list/contacts-list') { /* 这里我们就拿到了上一个页面的页面对象, 这里其实我们就可以使用lastPage做很多事情了, 例如直接操作lastPage.data, 修改上一个页面的数据 或者调用这个页面内的方法, 我上一个页面预留了一个更新方法, 所以这里就直接用上一个页面调用数据刷新的方法, 我这里给赋值, 就可以携带数据回上一个页面了 */ lastPage.updateContactList(this.data.contacts, this.data.idx) // 返回上一个页面 wx.navigateBack() } }, [代码] 二 使用eventChannel来逆向传值 B->A [代码]// B页面 _onClickComplete: function (e) { let eventChannel = this.getOpenerEventChannel() // updateAddressListData 这个方法需要上一个页面的支持, 上一个页面在navigateTo方法中的events数据中定义这个方法来接收数据 eventChannel.emit('updateAddressListData', this.data.address, this.data.idx) wx.navigateBack() }, // A页面需要的支持 _onClickCell: function (e) { wx.navigateTo({ url: '/pages/address-edit/address-edit', events: { // 这里用来接收后面页面传递回来的数据 updateAddressListData: (address, index) => { // 这里处理数据即可 } } }) } [代码] 代码片段
2020-07-21 - 小程序页面通信、数据刷新、事件总线 、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)
2019-08-04