利用watch在小程序中實現全局狀態共享

問題

在以前開發微信小程序的時候,獲取用戶信息、openid還有地理位置這些信息的時候,都是採用Promise的方式異步獲取,可是這樣的話在頁面和App.js中都獲取就可能形成請求重複的問題。javascript

好比爲了在每一個頁面都能獲取到這些共享信息,都會選擇在App.js中進行獲取,而後在頁面級進行獲取,這兩次獲取的時間間隔較小時就可能致使前一個請求還未獲取到數據,後一個請求就會再次進行獲取,這樣就產生了兩次請求。html

還有一個問題就是書寫麻煩(雖然也能經過async await簡化),好比vue

onLoad() {
    app.getUserInfo()
    .then(userInfo => {
        
    }).catch(err => { /* 錯誤處理 */ });
    
    // 若是同時須要userInfo和openid,可能就是以下形式:
    Promise.all([app.getUserInfo(), app.getOpenid()])
    .then(res => {
        
    }).catch(err => { /* 錯誤處理 */ });
}
複製代碼

正好週末的時候忽然想到了vue的watch語法,利用一些相關的知識,就能夠解決這個麻煩的問題了。java

解決思路

雙向綁定

vue的雙向綁定原理,3.0將會採用Proxy監聽數據變化,不過考慮到小程序這邊的Proxy兼容性我不知道,因此採用了2.0的Object.defineProperty來監聽數據的變化。git

主要仍是攔截設置的操做,在進行賦值時,將新舊值通知至監聽者。es6

觀察者模式

在頁面級的onLoad監聽app.globalData各個鍵名的事件,而在app.js的onLoad中則使用Object.defineProperty從新定義app.globalData,這樣一旦app.globalData相應的鍵值發生了變化,就會通知監聽的頁面該值發生了變化。github

模塊化的引用

觀察者模式導出的是一個對象(類實例),而不是一個類,因此在導入的時候這個對象是共享的,就能夠經過這個對象將app.js和其餘頁面聯繫起來。npm

至於模塊加載的實質,ES6模塊加載的機制,與CommonJS模塊徹底不一樣。感興趣的能夠去看看這個小程序

封裝Page

小程序的Page函數自己是不支持watch,可是咱們能夠自定義一個函數,進行參數合併就能夠了。微信小程序

在頁面onLoad時先遍歷watch屬性,對app.globalData進行監聽,能夠參考vue的watch用法。

頁面onUnload時就會進行銷燬,此時也應該取消監聽,這些我都封裝過了,不用手動處理了。

有了這些思路,用不了多久,一個雛形就出來了,通過手動測試,感受沒什麼問題,我就發佈到npm了,你們感興趣的能夠安裝體驗一下。

安裝

npm i wx-watch -S --production
複製代碼

使用

// app.js
var { watchData, } = require('/miniprogram_npm/wx-watch/index.js');

App({
  onLaunch() {
    this.watchData(); /* 監聽this.globalData的變化,並觸發事件,其餘頁面監聽的值必須在globalData中預先定義,不然沒法監聽 */
  },
  watchData,
  globalData: {
    userInfo: null,
  }
});

// 其餘須要監聽globalData的頁面.js
var { getPage } = require('../../miniprogram_npm/wx-watch/index.js');
const app = getApp();

/** * getPage(頁面參數,app) app必傳,由於封裝的時候訪問不到,就只能傳參了 */
getPage({
  watch: {
    userInfo(userInfo, oldUserInfo) {
      console.log(`來自app.glodalData的userInfo`);
    }
  },
  // 其餘參數
}, app)
複製代碼

github: github.com/ma125120/wx…

要是用的還行,歡迎star。

要是有問題,歡迎提交issue。

相關文章
相關標籤/搜索