基於Proxy的小程序狀態管理


摘要: 小程序狀態管理。

Fundebug經授權轉載,版權歸原作者所有。

微信小程序的市場在進一步的擴大,而背后的技術社區仍在摸索着最好的實踐方案。我在幫助Nike,沃爾瑪以及一些創業公司開發小程序后,依舊認為使用小程序原生框架是一個更高效,穩定的選擇,而使用原生框架唯獨缺少一個好的狀態管理庫,如果不引入狀態管理則會讓我們在模塊化,項目結構以及單元測試上都有些捉襟見肘。

目前相對比較穩健的做法是針對redux或者mobx做一個adaptor應用到小程序中,但這樣需要自己想辦法打包引入外部庫,還要想怎么去寫這個adaptor,總顯得有些麻煩。於是我迸發出一個想法去寫一個專用於小程序的狀態管理庫,它使用起來足夠簡單並且可以通過小程序自己的npm機制安裝。

目前我已經用這個開源庫開發了兩個電商小程序,在提高我開發效率的同時亦保證了程序的性能,所以接下來我想談談這背后的理念以啟發更多開發者嘗試新的解決方案。

基於Proxy的狀態管理實現

Proxy在小程序中已經得到了足夠好的支持,目前並沒有發現在任何iPhone或者Android上不能使用Proxy的情況。而基於Proxy的狀態管理其實也就是訂閱監聽的模式,一方面監聽數據的變化,另一方面將這些變化傳達給訂閱的小程序頁面。

舉一個比較常見的例子,當一個用戶從自己的主頁進入用戶編輯頁面,然后更改了自己的用戶名點擊保存后,用戶主頁和用戶編輯頁上的用戶名這時候都應該被更新。這背后的程序邏輯則是:更新這個行為將觸發Proxy去通知狀態管理庫,然后狀態管理庫負責檢查此時還在頁面棧中的所有頁面,更新訂閱了用戶名這個數據的頁面,如下圖:

Part1: 監聽數據變化

監聽數據變化其實就是監聽各個Store的屬性變化,實現上就是在各個Store前面加了一層Proxy,用更直觀的圖片來表示就是這樣:

當一個Store被觀察以后,它的屬性就都變成了Proxy實例,當這個屬性值是Object或者Array的時候,它內部的值也會被包裝成Proxy實例,這樣無論多深層的數據變動都能被監聽到。
而在Proxy的后面,Store的屬性其實是被另一套數據(紫色部分)所維護,這套數據不負責監聽,它就是純數據,針對屬性的任何變動最后都會應用到這套數據上來,它的作用是維護和返回最新的數據。

實現細節: https://github.com/wwayne/min...

Part2: 頁面數據綁定

因為小程序每個頁面的js都是向Page中傳遞一個對象,這就讓我們有機會包裝這個對象,從而實現:

  1. 進入頁面后,將頁面保存在頁面棧中
  2. 將來自狀態管理庫的數據映射到這個頁面的data上來
  3. 頁面退出時,將頁面從頁面棧中移除

實現細節: https://github.com/wwayne/min...

Part3: 頁面訂閱更新

當數據被監聽到變化后,我們需要依次做兩件事,先是找到所有存儲在頁面棧里的頁面,然后根據各個頁面訂閱的數據來檢查變化,如果有變化就通知這些頁面,從而讓它們去觸發setData更新頁面。

實現細節:https://github.com/wwayne/min...

使用狀態管理的例子

有了狀態管理庫,現在我們就來實現一開始舉例的更新用戶信息的操作,我們的文件路徑如下:

stores/
  user.js
pages/
  userEdit/
     index.js
     index.wxml

1. 首先我們創建一個Store保存用戶的信息,並且監聽它的變化:

// stores/user.js
import { observe } from 'minii'

Class UserStore {
  constructor () {
     this.name = 'bob'
  }

  changeName (name) {
     this.name = name
  }
}

export default observe(new UserStore(), 'user')

2. 接着在我們的小程序頁面訂閱Store的信息

// pages/userEdit/index.js
import { mapToData } from 'minii'
import userStore from '../../stores/user'

const connect = mapToData(state => (({
  myName: state.user.name
}))
Page(connect({
  updateNameToJames () {
    userStore. changeName('james')
  }
}))

3. 完成,現在可以在頁面中使用和更新數據了

// pages/userEdit/index.wxml
<text>{{ myName }}</text>
<button bindtap="updateNameToJames">update name to James</button>

最后

小程序因為有體積的限制,所以我希望在代碼量上也盡量做到輕量和便捷,所以目前這個狀態管理庫並沒有太多很復雜的功能,在小程序打包后所占用的體積也不到1kb,頗有點夠用就好的意思。

我也已經用它開發了兩款小程序,在經歷了一段時間的用戶使用后,我也更有信心說這個方案在小程序中是可行的。如果你有任何想法和建議,都歡迎告訴我。

項目Github: https://github.com/wwayne/minii


關於作者

Hi, 我是wwayne,是一名居住在上海的獨立軟件工程師,我正在開發我的新產品 talk-to-kim, 你可以在Github 或者專欄 一個人寫代碼找到我

關於Fundebug

Fundebug專注於JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有陽光保險、荔枝FM、掌門1對1、核桃編程、微脈、青團社等眾多品牌企業。歡迎免費試用!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM