訂閱發(fā)布模式
在開始之前先讓大家看一段代碼
wx.getUserInfo({
success: res => {
// 可以將 res 發(fā)送給后臺解碼出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是網絡請求,可能會在 Page.onLoad 之后才返回
// 所以此處加入 callback 以防止這種情況
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
---------------------------------------------------
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是網絡請求页徐,可能會在 Page.onLoad 之后才返回
// 所以此處加入 callback 以防止這種情況
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
}
想必開發(fā)過微信小程序的都認得吧苏潜。這是微信的一個回調方法,因為請求是異步的变勇,可能存在延遲恤左,當它還沒有儲存到 app.globalData
時,索引界面(第一個頁面)的 onload
已經執(zhí)行過了搀绣。此時飞袋,如果需要使用在 app.js
中的數(shù)據(jù),極大概率是空值链患,因此巧鸭,微信的做法是在索引界面的 onload
中判斷是否存在,不存在則繼續(xù)請求一次麻捻,然后將異步的值直接回調給自己纲仍。這種思路方法在我懵懂無知時簡直如同鳳姐得到那本規(guī)則世界的《故事會》一樣激動,以至于我常常覺得自己是盧瑟界的 chosen one 贸毕。
收郑叠,回到主題。今天我們將使用一種更加簡單明棍,直觀的模式去處理幾個小程序中經常遇到的麻煩事乡革。除了上面提到的,我想大家應該經常遇到的一項需求就是需要跳轉頁面的 自定義搜索
—— 在另一個頁面中輸入搜索的信息击蹲,點擊確定后署拟,wx.navigateBack()
返回上一頁面并且根據(jù)搜索內容顯示相應結果婉宰。我先來說一下我一開始的做法:
- 將自定義搜索頁輸入的數(shù)據(jù)儲存在
app.globalData
中 - 跳轉回上一頁面歌豺,在
onshow
里獲取app.globalData
中對應的數(shù)據(jù),進行后續(xù)的查詢操作 - 查詢完成心包,清空剛才存在
app.globalData
中的數(shù)據(jù)
這樣做雖然解決了這個問題类咧,但是每次展示頁面都需要onshow
無疑會增大開銷,數(shù)據(jù)重新setData
也會造成屏閃。 這還僅僅是一個頁面痕惋,如果消費頁面有多個就更加麻煩区宇。
另一個例子,在用戶登錄之后值戳,根據(jù) openid
創(chuàng)建一個 mqtt(socket)
長鏈接议谷。當信息到達時由鏈接頁面分別發(fā)給各個子頁面。比如堕虹,此時卧晓,鏈接發(fā)送給我一條信息 “空調制冷24℃”,我首先需要將空調頁組件化赴捞,在 properties 中創(chuàng)建一個入口用來接收信息,其次逼裆,使用observers 對入口進行監(jiān)聽,最后在鏈接頁引入組件赦政,把信息 setData
后通過傳給空調組件胜宇。天啊,我都說暈了恢着,如果我的鏈接頁和空調頁隔了多個頁面呢桐愉,如:主頁(鏈接頁)——>房間頁——> 設備列表頁——>空調頁。不敢想然评,層層去 setData
這是多大的開銷啊仅财。
鋪墊了這么久,下面該主角登場了(要不是屏幕正上方出現(xiàn)了“您的上機時間只剩5分鐘碗淌,請及時續(xù)費”盏求,我還能扯到雞吃盡米山,狗舔完面山)亿眠。
訂閱者模式碎罚,是將所有事件調度到一起,然后將事件分發(fā)給對應的訂閱者纳像。
如圖:
代碼實現(xiàn)
話不多說荆烈,都在酒里了。
class envet{
constructor(){
this.msg = {} //首先創(chuàng)建一個集合用來儲存所有訂閱的事件
}
/**
* subscribe 用來訂閱要回調的事件
* @param {*} key 事件的標識符
* @param {*} fn 回調函數(shù)
*/
subscribe(key, fn){
if(typeof fn != 'function') return //訂閱事件不是函數(shù)直接返回
if(!this.msg[key]) this.msg[key] = [] //不存在的事件創(chuàng)建
this.msg[key].push(fn) //添加函數(shù)到事件集合中竟趾。
}
/**
* 發(fā)布事件方法憔购,用來通知訂閱的事件
*/
publish(){
//可能會疑問,為什么這里沒有key值岔帽。
//下面這樣獲取arguments中的第一個值就是key
let key = Array.prototype.shift.call(arguments)
let callBack = this.msg[key]
if(!callBack || !callBack.length) return //集合為空就返回
//來了玫鸟,這是重點。
callBack.forEach(item=>{
item.apply(this,arguments) //遍歷集合下的每個函數(shù)犀勒,依次將arguments回調給函數(shù)
//其實屎飘,這里寫成 item.apply(null,arguments) 也行妥曲,我們需要的僅僅是arguments
})
}
/**
* 事件刪除,傳入key值(fn選填)
* @param {*} key
* @param {*} fn
*/
remove(key,fn){
let fns = this.msg[key]
if(!fns || !fns.length) return //空的話返回
if(!fn) delete this.msg[key] //fn不填的話直接把這個集合刪了
else{
for(let i=0;i<fns.length;i++){
let _item = fns[i]
if(_item === fn || _item.fn === fn){
fns.splice(i,1) //遍歷集合钦购,如果相同的話檐盟,splice
break
}
}
}
}
}
就是這么簡單,原理就是將 arguments
回調給訂閱的函數(shù)押桃。
嘗試效果
在 app.js
中創(chuàng)建:
在 pageA
頁面訂閱:
跳轉 pageB
頁面發(fā)布 test1
:
打印臺輸出:
此時我們看到 app.envet
中 msg
里有我們剛訂閱的兩個函數(shù)葵萎。
我們繼續(xù)跳轉 pageC
發(fā)布一條信息:
此時打印臺輸出: