微信小程式全域性狀態的深入講解
前言
在微信小程式中,可以利用 App.js 的 globalData 作為中間橋樑,在 Page, Component 之間,包括頁面與頁面,頁面與元件,元件與元件之間傳遞需要傳遞的資訊。但是,我們不能及時的知道 globalData 下的變化,在新建小程式的官方的預設事例中,獲取 UserInfo 這一網路操作有延遲的,為此寫了很多不必要的程式碼。就連官方案例都存在這一情況,相信在開發中你也會遇到類似的情況。在本文中將介紹如何解決這一類問題。
需求分析
相信以下情況是我們在沒有全域性狀態管理下常有的操作:
- 在 Page,Component 的 OnLoad,Attached 兩個生命週期鉤子函式中,進行一些從 App 的 globalData 賦值一些已經存在的屬性到頁面或元件中的 data 中。
- 在最開始就存在一些非同步的網路請求,獲取的資料用於全域性,剛開始可能這個 globalData 還沒有相關屬性,直到請求成功,才把相關屬性新增到 globalData,而這時 Page 的從 globalData 的賦值操作可能已經完成了,只不過是 undefined,為此需要進一步的判斷再進行賦值到 Page,Component 中。如果只是一兩個這個還說很簡單的,但是多個頁面,或者多個變數都需要賦值的話,我想你會拒絕並尋找偷懶的辦法。
- 一些在頁面和元件從 globalData 賦值的變數不僅是用於判斷、展示,我們可能還需要依據使用者互動而改變變數的值,那麼在其他頁面,其他元件中同樣的變數也需要統一改變。
以上情況我們可提出以下幾點需求:
- 在頁面,元件初始載入時,儘早的從 globalData 獲取並賦值到頁面,元件所需要的一些屬性
- 及時的獲取一些 globalData 某一屬性的變化,並進行一些後續相關操作 程式設計客棧
- 在改變 Page,Component 的值的同時,其他頁面,元件也進行一樣的改變
下面是需求的原始程式碼
// app.js App({ globalData: { userInfo: null },onLaunch(){ wx.getSetting({ success: res => { if(res.authSetting['scope.userInfo']){ wx.getUserInfo({ success: res => { this.globalData.userInfo = res.userInfo // 需求2 if (this.userInfoReadyCallback) { // 存在此回撥函式,意味著 page 執行了 onLoad // 且沒有獲取到 userInfo 並賦值到 page 的 data 中 // 執行此回撥函式,賦值到相應的頁面中 this.userInfoReadyCallback(res) } } }) } } }) } })
// Pages/index/index.js const app = getApp() Page({ // ... onLoad(options){ // 需求1 const userInfo = app.globalData.userInfo userInfo && this.setData({useInfo}) // 需求2 // 如果沒有獲取到 app.globalData.userInfo // 意味還未執行 wx.getUserInfo 的回撥函式 // 給 app 新增響應的一個回撥函式,繫結此時的 this 到回撥函式 userInfo || app.userInfoReadyCallback = res => { this.setData({ userInfo: res.userInfo }) delete app.userInfoReadyCallback } } })
這是官方小程式案例的程式碼,我只做了一點修改,這裡只是展示了需求 2 ,globalData 屬性從無到有時執行頁面設定的回撥函式,並沒有實現每一次都會執行回撥函式,需求 3 的程式碼比較複雜,不在此展示。
我們可以思考,以上幾點需求需要實現的,一定要有的程式碼有哪些。可以發現,需求 1 和需求 3 主要就是頁面,元件初始化,和 globalData 屬性被改變時都需要使用 this.setData 方法,只不過每次 this 的指向的例項不同。而需求 2 則是應該存在一個回撥函式,且回撥函式的 this 也應該指向相應的例項,在 globalData 屬性被改變時執行這些回撥函式。
從時間點來看,我們有兩個,一個是頁面,元件初始化,一個是 globalData 屬性改變時,那麼第一個時間點,我們可以考慮到小程式的生命週期的鉤子函式,onLoad 和 attached,在這兩個時間點執行 this.setData 的操作。而 globalData 屬性的改變都是我們主動或者使用者事件而產生的,就是可以看作這一操作是一個對 globalData 某個屬性的事件,而這個事件發生後再去執行一些寫好的回撥函式。
從操作物件來看,基本都是頁面和元件的例項 this,以及 app.globalData。
需求理論性總結
綜上,我們可以在初始化時,進行自動的 this.setData(不用自己手動),和儲存 this(用於事件執行時指向相應的例項),儲存相應的回撥函式為事件(事件就是未執行的函式),在需要時主動觸發這個事件即可。那麼可以看到,整個流程下來,我們需要一個橫跨 app,page,component 之間的一個變數,用於劫持初始化的鉤子函式,進行自動化賦值,儲存相應的事件,暴露一個事件觸發的介面。
紙上得來終覺淺,絕知此事要躬行www.cppcns.com
看到這裡,相信你已經有一定的瞭解全域性狀態管理,那麼到底如何實現呢?在這裡,我要強調,如果你閱讀此文後對此有一定的瞭解了,我說的思路,那麼你一定要自己嘗試實現出程式碼,不管是否好壞,總是比沒有實現的好,在自己實現中也許有更多的收穫。下面以上上面案例展示一下簡單的實現程式碼,給沒看太明白的一個思路。在下次我會寫一遍相關程式碼實現的講解,應該會有。
// app.js class Store { constructor(app){ this['event'] = {} this.app = app } autoSet(globalData,instance){ const instanceData = {} for (let prop of globalData){ instanceData[prop] = this.app.globalData[prop] const callBack = (newValue) => { instance.setData({[prop]: newValue}) instance.watch[prop] && instance.watch[prop].call(instance,newValue) } this.addEvent(prop,callBack) instance.setData(instanceData) callBack(instanceData[prop]) delete instance.watch dewww.cppcns.comlete instance.g程式設計客棧lobalData } } addEvent(eventName,callBack){ this.event[eventName] = this.event[eventName] || [] this.event[eventName].push(callBack) } dispatch(eventName,newValue){ this.app.globalData[eventName] = newValue this.event[eventName] && this.event[eventName].forEach(item => item(newValue)) } } App({ globalData: { userInfo: null },onLaunch(){ // new 一個例項並儲存到小程式 app 中,用於全域性呼叫 this.store = new Store(this) wx.getSetting({ success: res => { if(res.authSetting['scope.userInfo']){ wx.getUserInfo({ success: res => { // 獲取到 userInfo 後,觸發事件 this.store.dispatch('userInfo',res.userInfo) } }) } } }) } })
// Pages/index/index.js const app = getApp() Page({ // ... data: { userName: null },// globalData 陣列用於自動賦值 globalData: ['userInfo'],// 監聽相應的 globalData 屬性,設定回撥函式 watch: { userInfo(userInfo){ console.log('userInfo 更新啦',this) this.setData({userName: userInfo.nickName}) } },onLoad(options){ // 傳入此 globalData,和例項,設定該例項需要的 data,建立事件 app.store.autoSet(this.globalData,this) // 其他你想做的... } })
上面的程式碼並沒有劫持鉤子函式,只是額外在函式開始時執行了繫結函式,而且也沒有頁面http://www.cppcns.com銷燬時,釋放記憶體的操作。還是有許多可優化的地方,這些都留到下一次講解。
總結
到此這篇關於微信小程式全域性狀態的文章就介紹到這了,更多相關小程式全域性狀態內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!