微信小程式全域性狀態管理,並提供Vuex的開發體驗

SBDavid發表於2019-03-02

1. 概要

微信小程式的開發體驗類似vue和react,但是卻沒有提供全域性狀態管理的機制,所以狀態的共享只能通過屬性傳遞的方式來實現。這種做法在小規模的應用中尚可以滿足開發效率,但是在複雜的應用中元件的巢狀層次很深,屬性傳遞的路徑過長。

於是我就想利用小程式Page中的data物件來構建一個全域性store,這個store滿足一下幾點需求:

  • store可以被當前頁面中任意一個元件訪問,並且這種訪問時直接的而不是通過屬性傳遞。
  • 全域性store對於元件是相應式的,也就是說store的變化可以使元件發生重繪。
  • 頁面和元件都可以修改store的狀態,並且這種修改不破壞原來的響應式。
  • 提供類似Vuex的開發體驗,減小學習成本。

先附上原始碼 github地址

2. 使用

我們先跳過原理來看使用方法。

2.1 安裝

將Store.js放入微信小程式專案的資料夾中,例如/lib/Store.js。

2.2 建立Page物件

這裡我們通過wxappStore.createPage來建立。對比一下Store.js和原來的建立方法的區別

// 原來的建立方法
Page({
  data: {
    message: ``
  },
  onLoad: function () {
    this.setData({
        message: `hello world`
    })
  }
})
複製程式碼
// 增加全域性狀態管理之後
import wxappStore from "../../lib/Store.js";

Page(wxappStore.createPage({
    // 第一個引數和原來傳入Page方法的option沒有區別。其中的data會作為全域性共享物件來使用。
    data: {
        message: ``
    },
    onLoad: function () {
        // 通過dispatch方法,進行一個非同步操作。  
        this.store.dispatch({ 
            name: `testAction`,
            payload: `hello world`
        });
        // 通過commit方法,修改全域性狀態。
        this.store.commit({ 
            name: `testMutation`,
            payload: `hello world`
        });
    }
}, 
// 第二個引數是一個物件,其中包含mutations和actions
{
    mutations: {
        testMutation: function({ setData, payload, data }) {
            setData({
                message: payload
            });
        }
    },
    actions: {
        testAction: function ({ commit, payload, data }) {
            setTimeout(() => {
                commit({
                    name: `testMutation`,
                    payload: payload
                });
            });
        }
    }
}))
複製程式碼

wxappStore.createPage方法有兩個引數。

第一個引數和原來傳入Page方法的option沒有區別。其中的data會作為全域性共享物件來使用。

第二個引數是一個物件,其中包含mutationsactions

2.3 使用mutation

mutation和Vuex中的mutation類似,它通過同步的方式修改狀態。可以通過commit呼叫。

2.3.1 定義mutation

mutations在wxappStore.createPage的第二個引數中定義,它用於修改全域性狀態。mutation通常同步的。mutation方法的引數是一個物件,包含三個屬性:

  • setData function: 用來修改全域性狀態,在微信小程式中直接修改狀態不會觸發頁面重匯。
  • payload object:修改的狀態,可以是一個物件,也可以是String等基礎資料型別
  • data object:當前狀態
mutations: {
    testMutation: function({ setData, payload, data }) {
        setData({
            message: payload
        });
    }
},
複製程式碼

2.3.2 呼叫mutation

通過commit方法呼叫mutation,它的引數是一個物件,包含兩個屬性:

  • name String:mutation的名稱
  • payload Object:需要修改的狀態,和Vuex的payload類似。
this.store.commit({ 
    name: `testMutation`,
    payload: `hello world`
});
複製程式碼

2.4 使用action

action和Vuex中action概念類似,通常包含非同步操作,在非同步操作完成後進行commit操作。

2.4.1 定義action

action方法的引數是一個引數,包含3個屬性:

  • commit function:執行commit操作
  • payload Object:資料物件,和Vuex型別
  • data Object:當前狀態
actions: {
    testAction: function ({ commit, payload, data }) {
        setTimeout(() => {
            commit({
                name: `testMutation`,
                payload: payload
            });
        });
    }
}
複製程式碼

2.4.2 呼叫action

通過dispatch方法呼叫action,它的引數是一個物件,包含兩個屬性:

  • name String:action的名稱
  • payload Object:需要修改的狀態,和Vuex的payload類似。
this.store.dispatch({ 
    name: `testAction`,
    payload: `hello world`
});
複製程式碼

2.5 建立Component

在Component中我們需要完成兩項工作

第一將全域性狀態繫結到當前元件的data屬性上,並將元件的data屬性繫結到頁面元素上。

第二元件需要使用commit或者dispatch完成全域性狀態的修改。

這裡Store.j通過wxappStore.createComp來建立Component,它會通過代理的方式為Component實現全域性狀態管理的功能。

import wxappStore from "../lib/Store.js";

Component(wxappStore.createComp({
  data: {
    localtimeData: ``
  },
  ready: function () {
    // 繫結全域性狀態
    this.getGlobalData({ globalDataKey: `localtime`, localDataKey: `localtimeData` });

    // 改變全域性狀態  
    this.store.commit({
        name: `testMutation`,
        payload: (new Date()).toLocaleTimeString()
    })
  }
}))

複製程式碼
<view>讀取全域性狀態:{{localtimeData}}</view>
複製程式碼

2.5.1 全域性狀態繫結

全域性狀態繫結通過getGlobalData這個例項方法實現,這個方法並不在小程式的執行環境中,它是Store.js執行的過程中插入到Component例項中的。

getGlobalData 不能再created回撥中呼叫,應為component的例項方法setData不能再created中呼叫。

getGlobalData的引數是一個物件,包含兩個屬性:

  • globalDataKey String:這個屬性表示需要全域性狀態的屬性名,這個全域性狀態將於component的本地狀態繫結。
  • localDataKey String:這個屬性表示本地狀態的屬性名,這個本地狀態將於全域性狀態繫結。
// 繫結全域性狀態
this.getGlobalData({ globalDataKey: `localtime`, localDataKey: `localtimeData` });
複製程式碼

2.5.2 改變全域性狀態

可以使用store.commit或者store.dispatchstore並不是小程式的執行環境中內建的,同樣是通過Store.js插入到每一個component例項中。它的使用方法和Page中的類似。

3. 這套框架的不足

  • Store.js借用了Page物件的data屬性來完成全域性狀態管理,所以data屬性的職責並不單一。data屬性兼具了頁面ViewModel的功能和全域性狀態的功能。但是Page中的data屬性本來也具有全域性意義,所以兩者的衝突並不大。

  • component中的data屬性職責並不單一。它兼具了本地屬性的功能和繫結全域性狀態的功能。而且直接通過setData修改component中的data並不能觸發全域性狀態的改變,因為data的作用域僅限於當前component,必須通過store.commit或者store.dispatch觸發發全域性狀態的改變。

  • 如果你發現其他問題,歡迎留言,我們共同進步!

4. 寫完好累,原理等下次更新吧。。。。

相關文章