微信小程序全局狀態(tài)的深入講解
前言
在微信小程序中,可以利用 App.js 的 globalData 作為中間橋梁,在 Page, Component 之間,包括頁(yè)面與頁(yè)面,頁(yè)面與組件,組件與組件之間傳遞需要傳遞的信息。但是,我們不能及時(shí)的知道 globalData 下的變化,在新建小程序的官方的默認(rèn)事例中,獲取 UserInfo 這一網(wǎng)絡(luò)操作有延遲的,為此寫(xiě)了很多不必要的代碼。就連官方案例都存在這一情況,相信在開(kāi)發(fā)中你也會(huì)遇到類(lèi)似的情況。在本文中將介紹如何解決這一類(lèi)問(wèn)題。
需求分析
相信以下情況是我們?cè)跊](méi)有全局狀態(tài)管理下常有的操作:
- 在 Page,Component 的 OnLoad,Attached 兩個(gè)生命周期鉤子函數(shù)中,進(jìn)行一些從 App 的 globalData 賦值一些已經(jīng)存在的屬性到頁(yè)面或組件中的 data 中。
- 在最開(kāi)始就存在一些異步的網(wǎng)絡(luò)請(qǐng)求,獲取的數(shù)據(jù)用于全局,剛開(kāi)始可能這個(gè) globalData 還沒(méi)有相關(guān)屬性,直到請(qǐng)求成功,才把相關(guān)屬性添加到 globalData,而這時(shí) Page 的從 globalData 的賦值操作可能已經(jīng)完成了,只不過(guò)是 undefined,為此需要進(jìn)一步的判斷再進(jìn)行賦值到 Page,Component 中。如果只是一兩個(gè)這個(gè)還說(shuō)很簡(jiǎn)單的,但是多個(gè)頁(yè)面,或者多個(gè)變量都需要賦值的話(huà),我想你會(huì)拒絕并尋找偷懶的辦法。
- 一些在頁(yè)面和組件從 globalData 賦值的變量不僅是用于判斷、展示,我們可能還需要依據(jù)用戶(hù)交互而改變變量的值,那么在其他頁(yè)面,其他組件中同樣的變量也需要統(tǒng)一改變。
以上情況我們可提出以下幾點(diǎn)需求:
- 在頁(yè)面,組件初始加載時(shí),盡早的從 globalData 獲取并賦值到頁(yè)面,組件所需要的一些屬性
- 及時(shí)的獲取一些 globalData 某一屬性的變化,并進(jìn)行一些后續(xù)相關(guān)操作
- 在改變 Page,Component 的值的同時(shí),其他頁(yè)面,組件也進(jìn)行一樣的改變
下面是需求的原始代碼
// 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) { // 存在此回調(diào)函數(shù),意味著 page 執(zhí)行了 onLoad // 且沒(méi)有獲取到 userInfo 并賦值到 page 的 data 中 // 執(zhí)行此回調(diào)函數(shù),賦值到相應(yīng)的頁(yè)面中 this.userInfoReadyCallback(res) } } }) } } }) } })
// Pages/index/index.js const app = getApp() Page({ // ... onLoad(options){ // 需求1 const userInfo = app.globalData.userInfo userInfo && this.setData({useInfo}) // 需求2 // 如果沒(méi)有獲取到 app.globalData.userInfo // 意味還未執(zhí)行 wx.getUserInfo 的回調(diào)函數(shù) // 給 app 添加響應(yīng)的一個(gè)回調(diào)函數(shù),綁定此時(shí)的 this 到回調(diào)函數(shù) userInfo || app.userInfoReadyCallback = res => { this.setData({ userInfo: res.userInfo }) delete app.userInfoReadyCallback } } })
這是官方小程序案例的代碼,我只做了一點(diǎn)修改,這里只是展示了需求 2 ,globalData 屬性從無(wú)到有時(shí)執(zhí)行頁(yè)面設(shè)置的回調(diào)函數(shù),并沒(méi)有實(shí)現(xiàn)每一次都會(huì)執(zhí)行回調(diào)函數(shù),需求 3 的代碼比較復(fù)雜,不在此展示。
我們可以思考,以上幾點(diǎn)需求需要實(shí)現(xiàn)的,一定要有的代碼有哪些??梢园l(fā)現(xiàn),需求 1 和需求 3 主要就是頁(yè)面,組件初始化,和 globalData 屬性被改變時(shí)都需要使用 this.setData 方法,只不過(guò)每次 this 的指向的實(shí)例不同。而需求 2 則是應(yīng)該存在一個(gè)回調(diào)函數(shù),且回調(diào)函數(shù)的 this 也應(yīng)該指向相應(yīng)的實(shí)例,在 globalData 屬性被改變時(shí)執(zhí)行這些回調(diào)函數(shù)。
從時(shí)間點(diǎn)來(lái)看,我們有兩個(gè),一個(gè)是頁(yè)面,組件初始化,一個(gè)是 globalData 屬性改變時(shí),那么第一個(gè)時(shí)間點(diǎn),我們可以考慮到小程序的生命周期的鉤子函數(shù),onLoad 和 attached,在這兩個(gè)時(shí)間點(diǎn)執(zhí)行 this.setData 的操作。而 globalData 屬性的改變都是我們主動(dòng)或者用戶(hù)事件而產(chǎn)生的,就是可以看作這一操作是一個(gè)對(duì) globalData 某個(gè)屬性的事件,而這個(gè)事件發(fā)生后再去執(zhí)行一些寫(xiě)好的回調(diào)函數(shù)。
從操作對(duì)象來(lái)看,基本都是頁(yè)面和組件的實(shí)例 this,以及 app.globalData。
需求理論性總結(jié)
綜上,我們可以在初始化時(shí),進(jìn)行自動(dòng)的 this.setData(不用自己手動(dòng)),和保存 this(用于事件執(zhí)行時(shí)指向相應(yīng)的實(shí)例),存儲(chǔ)相應(yīng)的回調(diào)函數(shù)為事件(事件就是未執(zhí)行的函數(shù)),在需要時(shí)主動(dòng)觸發(fā)這個(gè)事件即可。那么可以看到,整個(gè)流程下來(lái),我們需要一個(gè)橫跨 app,page,component 之間的一個(gè)變量,用于劫持初始化的鉤子函數(shù),進(jìn)行自動(dòng)化賦值,存儲(chǔ)相應(yīng)的事件,暴露一個(gè)事件觸發(fā)的接口。
紙上得來(lái)終覺(jué)淺,絕知此事要躬行
看到這里,相信你已經(jīng)有一定的了解全局狀態(tài)管理,那么到底如何實(shí)現(xiàn)呢?在這里,我要強(qiáng)調(diào),如果你閱讀此文后對(duì)此有一定的了解了,我說(shuō)的思路,那么你一定要自己嘗試實(shí)現(xiàn)出代碼,不管是否好壞,總是比沒(méi)有實(shí)現(xiàn)的好,在自己實(shí)現(xiàn)中也許有更多的收獲。下面以上上面案例展示一下簡(jiǎn)單的實(shí)現(xiàn)代碼,給沒(méi)看太明白的一個(gè)思路。在下次我會(huì)寫(xiě)一遍相關(guān)代碼實(shí)現(xiàn)的講解,應(yīng)該會(huì)有。
// 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 delete instance.globalData } } 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 一個(gè)實(shí)例并保存到小程序 app 中,用于全局調(diào)用 this.store = new Store(this) wx.getSetting({ success: res => { if(res.authSetting['scope.userInfo']){ wx.getUserInfo({ success: res => { // 獲取到 userInfo 后,觸發(fā)事件 this.store.dispatch('userInfo', res.userInfo) } }) } } }) } })
// Pages/index/index.js const app = getApp() Page({ // ... data: { userName: null }, // globalData 數(shù)組用于自動(dòng)賦值 globalData: ['userInfo'], // 監(jiān)聽(tīng)相應(yīng)的 globalData 屬性,設(shè)置回調(diào)函數(shù) watch: { userInfo(userInfo){ console.log('userInfo 更新啦', this) this.setData({userName: userInfo.nickName}) } }, onLoad(options){ // 傳入此 globalData,和實(shí)例,設(shè)置該實(shí)例需要的 data,創(chuàng)建事件 app.store.autoSet(this.globalData, this) // 其他你想做的... } })
上面的代碼并沒(méi)有劫持鉤子函數(shù),只是額外在函數(shù)開(kāi)始時(shí)執(zhí)行了綁定函數(shù),而且也沒(méi)有頁(yè)面銷(xiāo)毀時(shí),釋放內(nèi)存的操作。還是有許多可優(yōu)化的地方,這些都留到下一次講解。
總結(jié)
到此這篇關(guān)于微信小程序全局狀態(tài)的文章就介紹到這了,更多相關(guān)小程序全局狀態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
headjs實(shí)現(xiàn)網(wǎng)站并行加載但順序執(zhí)行JS
本文主要介紹如何使用head.js實(shí)現(xiàn)網(wǎng)站并行加載但順序執(zhí)行JS,提高網(wǎng)站加載速度。需要的朋友可以看下2016-11-11uni-app微信小程序使用echarts的詳細(xì)圖文教程
為了兼容小程序Canvas,ECharts提供了一個(gè)小程序的組件,用這種方式可以方便地使用ECharts,下面這篇文章主要給大家介紹了關(guān)于uni-app微信小程序使用echarts的相關(guān)資料,需要的朋友可以參考下2022-10-10JavaScript中變量提升導(dǎo)致未定義(undefined)的問(wèn)題及解決方法
在 JavaScript 中,變量提升(Hoisting)是一個(gè)相對(duì)常見(jiàn)的行為,尤其是當(dāng)你遇到 undefined 錯(cuò)誤時(shí),本文將詳細(xì)探討變量提升的概念、其對(duì)代碼執(zhí)行的影響以及如何避免因?yàn)樽兞刻嵘鴮?dǎo)致 undefined 的問(wèn)題,需要的朋友可以參考下2024-09-09原生JS實(shí)現(xiàn)列表子元素順序反轉(zhuǎn)的方法分析
這篇文章主要介紹了原生JS實(shí)現(xiàn)列表子元素順序反轉(zhuǎn)的方法,結(jié)合實(shí)例形式分析了javascript針對(duì)dom元素、數(shù)組reverse方法、innerHTML方法等列表元素順序翻轉(zhuǎn)相關(guān)操作技巧,需要的朋友可以參考下2018-07-07bootstrap模態(tài)框彈出和隱藏,動(dòng)態(tài)改變中間內(nèi)容的實(shí)例
今天小編就為大家分享一篇bootstrap模態(tài)框彈出和隱藏,動(dòng)態(tài)改變中間內(nèi)容的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08js實(shí)現(xiàn)超簡(jiǎn)單的展開(kāi)、折疊目錄代碼
這篇文章主要介紹了js實(shí)現(xiàn)超簡(jiǎn)單的展開(kāi)、折疊目錄代碼,通過(guò)javascript操作鼠標(biāo)點(diǎn)擊事件控制頁(yè)面元素樣式的動(dòng)態(tài)改變實(shí)現(xiàn)該功能,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-08-08JS使用正則表達(dá)式獲取小括號(hào)、中括號(hào)及花括號(hào)內(nèi)容的方法示例
這篇文章主要介紹了JS使用正則表達(dá)式獲取小括號(hào)、中括號(hào)及花括號(hào)內(nèi)容的方法,涉及javascript針對(duì)三種括號(hào)正則匹配的相關(guān)操作技巧,需要的朋友可以參考下2018-06-06微信 jssdk 簽名錯(cuò)誤invalid signature的解決方法
這篇文章主要介紹了微信 jssdk 簽名錯(cuò)誤invalid signature的解決方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01正則中的回溯定義與用法分析【JS與java實(shí)現(xiàn)】
這篇文章主要介紹了正則中的回溯定義與用法,結(jié)合實(shí)例形式分析了回溯的概念、功能并提供了JS與java實(shí)現(xiàn)方法,需要的朋友可以參考下2016-12-12JavaScript通過(guò)mouseover()實(shí)現(xiàn)圖片變大效果的示例
下面小編就為大家分享一篇JavaScript通過(guò)mouseover()實(shí)現(xiàn)圖片變大效果的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-12-12