在各类小程序中,咱们常常会遇到 这种状况
有一个 列表,点击列表中的一项进入详情,详情有个按钮,删除了这一项,这个时候当用户返回到列表页时,
发现列表中的这一项依然存在,这种状况,就是一个 `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 的环境,就能使用
复制代码
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,
b,
c
}
// 调用页面请求函数,刷新数据
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 小程序代码java
基本打包工具,这里使用很是优秀的开源库 typescript-library-starter,具体细节不展开ios
测试工具 使用 facebook 的 jestgit
build ci 使用 [travis-ci](www.travis-ci.org/)github
测试覆盖率上传使用 codecovtypescript
具体的其余细节你们能够看源码中的 package.json,这里就一一展开讲了,咱们来看具体实现npm
class EventBus {
private events: any[] = []
}
复制代码
interface EventBus {
// 监听,咱们须要知道一个事件名字,也须要一个 派发时的执行函数,同时,咱们返回一个
// id 给使用者,方便使用者移除 事件监听
on(name: string, execute: Function): string
// once 和 on在使用建立和使用时,没什么区别,惟一的区别就在 执行一次后移除,因此在
// 建立时 和 on 没有任何区别
once(name: string, execute: Function): string
// remove, 前面提到了咱们须要删除事件监听,那咱们就须要 事件名称,为了多个页面能够监
// 听同一个事件,因此咱们不能一次性把该事件监听所有移除
// 那么咱们就用到 建立 事件时的 id 了, 同时,咱们返回 咱们的事件中心,能够链式调用
remove(name: string, eventId?: string): EventBus
// emit 咱们须要告诉系统,咱们须要派发的事件名和所携带的参数,同时返回 事件实例
emit(name: string, ...args: any[]): EventBus
// find 函数返回一个联合类型,有可能存在 该事件,也有可能返回 null
find(name: string): Event | null
}
复制代码
// 每个东西,都有一个名字,方便记忆和寻找,咱们的事件
// 也须要一个 name,同时,咱们的每个事件,都有可能被监听 n 次,那么咱们就须要
// 每一个事件来有一个容器,存放每一个事件的执行者
interface Event {
// 名称
name: string
// 执行者容器
executes: Execute[]
}
// 咱们也须要肯定每一个执行者的类型,为了能精确的找到执行者,因此须要一个 id,这也是 用来
// 删除的id, 这里的 eventType 是来标示是不是一次性执行者, execute 则为每一个执行者
// 的执行函数
interface Execute {
id: string
eventType: EventType
execute: Function
}
复制代码
// 申明事件执行者的类型
type EventType = 1 | 2
enum EventTypeEnum {
// 普通事件
NORMAL_EVENT = 1,
// 一次性事件
ONCE_EVENT = 2
}
复制代码
class EventBus {
/** * 储存事件的容器 */
private events: Event[] = []
/** * on 新增事件监听 * @param name 事件名 * @param execute 回调函数 * @returns { string } eventId 事件ID,用户取消该事件监听 */
on(name: string, execute: Function): string {
// 由于 on 和 once 在新建上没什么区别,因此这里咱们统一使用 addEvent, 但为了区分 on 和 once,咱们传入了 EventType
return this.addEvent(name, EventTypeEnum.NORMAL_EVENT, execute)
}
/** * one 只容许添加一次事件监听 * @param name 事件名 * @param execute 回调函数 * @returns { string } eventId 事件ID,用户取消该事件监听 */
once(name: string, execute: Function): string {
// 同理 on
return this.addEvent(name, EventTypeEnum.ONCE_EVENT, execute)
}
}
复制代码
class EventBus {
/** * 添加事件的方法 * @param name * @param execute */
private addEvent(name: string, eventType: EventType, execute: Function): string {
const eventId = createUid()
const events = this.events
const event = this.find(name)
if (event !== null) {
event.executes.push({ id: eventId, eventType, execute })
return eventId
}
events.push({
name,
executes: [
{
id: eventId,
eventType,
execute
}
]
})
return eventId
}
}
复制代码
class EventBus {
/** * 查找事件的方法 * @param name */
find(name: string): Event | null {
const events = this.events
for (let i = 0; i < events.length; i++) {
if (name === events[i].name) {
return events[i]
}
}
return null
}
}
复制代码
class EventBus {
/** * remove 移除事件监听 * @param name 事件名 * @param eventId 移除单个事件监听需传入 * @returns { EventBus } EventBus EventBus 实例 */
remove(name: string, eventId: string): EventBus {
const events = this.events
for (let i = 0; i < events.length; i++) {
if (events[i].name === name) {
// 移除具体的操做函数
if (eventId && events[i].executes.length > 0) {
const eventIndex = events[i].executes.findIndex(item => item.id === eventId)
if (eventIndex !== -1) {
events[i].executes.splice(eventIndex, 1)
}
} else {
events.splice(i, 1)
}
return this
}
}
return this
}
}
复制代码
class EventBus {
/** * emit 派发事件 * @param name 事件名 * @param args 其他参数 * @returns { EventBus } EventBus EventBus 实例 */
emit(name: string, ...args: any[]): EventBus {
const events = this.events
for (let i = 0; i < events.length; i++) {
if (name === events[i].name) {
const funcs = events[i].executes
funcs.forEach((item, i) => {
item.execute(...args)
if (item.eventType === EventTypeEnum.ONCE_EVENT) {
funcs.splice(i, 1)
}
})
return this
}
}
return this
}
}
复制代码
// 不直接 new EventBus, 而是经过 一个工厂函数来建立实例, 参考 axios 源码
function createInstance (): EventBusInstance {
const bus = new EventBus()
return bus as EventBusInstance
}
const bus = createInstance()
// 扩展 create 方法,用于 使用者 建立新的 bus 实例
bus.create = function create () {
return createInstance()
}
复制代码
iny-bus 的核心代码,其实就这么多,总的来讲,很是少,可是能解决咱们在小程序中遇到的大量 通讯 和 数据刷新问题,是采用 各大平台小程序 原生开发时,页面通讯的不二之选,同时,100% 的测试覆盖率,确保了 iny-bus 在使用中的稳定性和安全性,固然,每一个库都是从简单走向复杂,功能慢慢完善,若是 你们在使用或者源码中发现了bug或者能够优化的点,欢迎你们提 pr 或者直接联系我json
最后,若是 iny-bus 给你提供了帮助或者让你有任何收获,请给 做者 点个赞,感谢你们 点赞axios