在各類小程序中,咱們常常會遇到 這種狀況
有一個 列表,點擊列表中的一項進入詳情,詳情有個按鈕,刪除了這一項,這個時候當用戶返回到列表頁時,
發現列表中的這一項依然存在,這種狀況,就是一個 `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