手把手教你在小程序里使用 Redux

微信小程序里使用 Redux 狀態(tài)管理

前言

前陣子一直在做小程序開發(fā)沉填,采用的是官方給的框架 wepy , 如果還不了解的同學可以去他的官網(wǎng)查閱相關資料學習醇锚;不得不說的是,這個框架確相比于傳統(tǒng)小程序開發(fā)模式確實方便很多酱吝,它的語法 Vue 的語法很像,可以實現(xiàn)組件化開發(fā)窍蓝,方面后面代碼的調整和維護...但是R傅摺!這個框架的坑也不是一點點吓笙,開發(fā)的時候總會遇到奇奇怪怪的問題淑玫,自己去踩吧,這樣你才能進步~~

廢話了這么多面睛,咳咳絮蒿,上面的都不是我們要討論的重點,我們今天的重點是—在小程序里使用 Redux 進行狀態(tài)管理叁鉴,Redux 是一個前端狀態(tài)管理的容器土涝,對于構建大型應用,對里面共享數(shù)據(jù)幌墓、狀態(tài)的管理非常方便但壮,學過 React 的同學對它應該不陌生,如果還不了解的同學常侣,不如進服瞧一瞧;

wepy 框架本身是支持 Redux 的蜡饵,我們在構建項目的時候,將 是否安裝 Redux 選擇 y 就好了胳施,會自動安裝依賴溯祸,運行項目后看官方給的demo確實是可以做到的,但是官方文檔里卻對這一塊只字不提舞肆,經(jīng)過我自己嘗試了一波焦辅,這才稍微摸清了它的使用方式,趕緊拿來與你們分享~

注意了椿胯,接下來劃重點了~

具體實現(xiàn)

運行我們的項目筷登,發(fā)現(xiàn)官網(wǎng)已經(jīng)給了我們一些 Redux 的使用方法,實際上主要是放在 store 文件夾下面了压状,我們現(xiàn)在來一探究竟~

step1

入口文件 index.js 仆抵,里面主要是 初始化 Redux , 其中 promiseMiddleware 是一個中間件种冬,方便后面 action 做異步處理~ reducers 是一個純函數(shù)镣丑,用于接受 Action 和當前 State 作為參數(shù),返回一個新的 State~

import { createStore , applyMiddleware } from 'redux'
import promiseMiddleware from 'redux-promise'
import reducer from './reducers'

const Store = createStore(
    reducer ,
    applyMiddleware(promiseMiddleware)
)

export default configStore => Store

step2

剩下三個文件夾分別是 types reducersactions 娱两,其中 types 用于定義我們要觸發(fā)的 action 的名稱莺匠,也就是表示 action的名稱,這里我定義了 counterlist 兩個 types 十兢,內容分別如下:

counter.js

export const INCREMENT = 'INCREMENT'

export const DECREMENT = 'DECREMENT'

export const ASYNC_INCREMENT = 'ASYNC_INCREMENT'

list.js

export const ADD = 'ADD'

export const REMOVE = 'REMOVE'

最后通過 types 文件夾的入口文件 index.js 將他們暴露出去~

export * from './counter'
export * from './list'

step3

reducers文件件存放我們的純函數(shù)趣竣,用來更改我們的狀態(tài) 摇庙, 他也有一個入口文件 index.js,定義如下:

    import { combineReducers } from 'redux'
    import counter from './counter'
    import list from './list'
    
    export default combineReducers({
        counter ,
        list
    })

首先將 counterlist 的分別引入進來遥缕,通過 redux 定義的 combineReducers 函數(shù)卫袒,將所有的 reducers 合并成一個整體,方便我們后面對其進行管理单匣!

那么 counterlist 對應的 reducer 分別是 什么樣的夕凝?我們直接看代碼:

counter.js

    import { handleActions } from 'redux-actions'
    import { INCREMENT , DECREMENT , ASYNC_INCREMENT } from '../types/counter'
    
    const defaultState  = {
        num: 0 ,
        asyncNum: 0
    }
    
    export default handleActions({
        [INCREMENT](state){
            return{
                ...state,
                num : state.num + 1
            }
        },
        [DECREMENT](state){
            return{
                ...state,
                num : state.num - 1
            }
        },
        [ASYNC_INCREMENT](state, action){
            return {
                ...state ,
                asyncNum : state.asyncNum + action.payload
            }
        }
    },defaultState)

里面涉及到了不少 ES6 的語法,不太明白的同學可以看看我之前寫的 關于 ES6 的文章~

我們介紹一下 counter.js 里面的 reducer 户秤, 首先引入了 handleActions 方法用來創(chuàng)建 actions 码秉, 它將多個相關的 reducer 寫在一起也是 ,方面后期維護鸡号,也方便后期通過 dispatch 來調用他們更改 state 里面的狀態(tài)转砖,它主要接收兩個參數(shù),第一個參數(shù)時候個大對象鲸伴,里面存放多個 reducer 府蔗, 第二個參數(shù)是初始化的時候 state 的狀態(tài)值,因此挑围,我們一開始就定義了 defaultState ;

接著礁竞,我們看看里面的 reducer 糖荒, 分別定義了 INCREMENT杉辙、 DECREMENTASYNC_INCREMENT 三個 reducer ,前兩個比較簡單捶朵,分別是對 state 里面的 num 值進行 加減操作 蜘矢, 最后一個是通過 action.payload 的值來對 asyncNum 的值進行異步操作的,具體怎么做到的综看,我們一會再看~

list.js 里定義的 reducer 跟上面類似品腹,我就不一一介紹了,直接貼代碼即可~

list.js

    import { handleActions } from 'redux-actions'
    import { ADD , REMOVE } from '../types/list'
    
    const defaultState = [
        {
            title : '吃飯' ,
            text : '今天我要吃火鍋'
        },
        {
            title : '工作' ,
            text : '今天我要學習Redux'
        }
    ]
    
    export default handleActions({
        [ADD]( state , action ){
            state.push(action.payload)
            return [...state]
        },
        [REMOVE]( state , action ){
            state.splice( action.payload , 1 );
            return [ ...state ]
    
        }
    },defaultState)

step4

我們終于走到這一步了红碑,到這里舞吭,你已經(jīng)離預期不遠啦,就剩一個 actions 文件件了析珊,毫不例外羡鸥,入口文件 index.js 如下:

index.js

    export * from './counter'

很簡單,只需要將所需的 action 導出即可~

這個里面我只定義了 counteraction 忠寻, 也就是為了剛才異步數(shù)據(jù) asyncNum 準備的~

counter.js

    import { ASYNC_INCREMENT } from '../types/counter'
    import { createAction } from 'redux-actions'
    
    export const asyncInc = createAction(ASYNC_INCREMENT,()=>{
        return new Promise(resolve=>{
            setTimeout(()=>{
                resolve(1)
            },1000)
        })
    })

這里跟 reducer 里面的要區(qū)分惧浴,這里是可以對數(shù)據(jù)進行一系列處理的,我們通過 createAction 創(chuàng)建一個 action , 該方法主要有兩個參數(shù)奕剃,第一個參數(shù) type 表示 action 的類型衷旅,第二個參數(shù) payloadCreator 是一個 function捐腿,處理并返回需要的 payload ;如果空缺柿顶,會使用默認方法茄袖。這里我們是延遲 1s 后返回一個 1

ok嘁锯,到此為止绞佩,你已經(jīng)基本完成了一個 redux 的容器~

接下來,就是展示它怎么使用的時候了~

step5

我們創(chuàng)建一個 index.wpy 的文件猪钮,這里我把代碼直接貼出來品山,然后慢慢來分析看看~

代碼如下:

    <template lang="wxml">
      <view class="container">
        <text>同步{{ num }}</text>
        <text>異步{{ asyncNum }}</text>
        <button @tap="increment" type="primary">加一</button>
        <button @tap="decrement" type="primary">減一</button>
        <button @tap="asyncIncrement" type="primary">異步加一</button>
    
        <button @tap="addList">添加</button>
    
        <view class="box">
            <view class="item" wx:for-items="{{ todoList }}" wx:key="index">
                <view class="title">{{ item.title }}</view>
                <view class="content">{{ item.text }}</view>
                <button type="primary" class="delete" @tap="delete({{index}})">刪除</button>
            </view>
        </view>
    
      </view>
    
    </template>
    
    <script>
        import wepy from 'wepy'
        import { connect } from 'wepy-redux'
        import { INCREMENT , DECREMENT } from '../store/types/counter'
        import { asyncInc } from '../store/actions'
    
        @connect({
            num(state){
                return state.counter.num;
            },
            asyncNum(state){
                return state.counter.asyncNum;
            }
        },{
            increment : INCREMENT ,
            decrement : DECREMENT ,
            asyncIncrement : asyncInc
        })
    
        export default class Index extends wepy.page {
    
        components = {}
    
        computed = {
            todoList(){
                return wepy.$store.getState().list;
            }
        }
    
        methods = {
            delete(index){
                wepy.$store.dispatch({ type : 'REMOVE' , payload : index })
                },
            addList(){
                wepy.$store.dispatch({ type : 'ADD' , payload : {
                    title : '學習' ,
                    text : '好好學習'
                }})
            }
        }
    
        onLoad () {
            console.log(wepy.$store.getState())
        }
    }
    </script>
    
    
    <style lang="less">
        text{
            display: block;
            text-align: center;
            margin: 10px auto;
        }
        button{
            width: 90%;
            display: block;
            margin: 10px auto;
        }
    
        .item{
            display: flex;
            align-items: center;
            text-align: center;
            padding: 0 15px;
            .title{
                font-size: 14px;
                line-height: 20px;
                margin: 10px auto;
            }
            .content{
                font-size: 15px;
                flex: 1;
            }
    
            .delete{
                width: 70px;
                height: 40px;
                line-height: 40px;
            }
        }
    </style>

不出意外,運行后烤低,你的小程序的界面會跟下面一樣————丑~

點一點看肘交,發(fā)現(xiàn)臥槽,很牛逼扑馁,有木有~

ok~ 我們一起看看上面的代碼是怎么做的~

樣式結構方面我們這里不做討論涯呻,主要看 js 部分,其中
import { INCREMENT , DECREMENT } from '../store/types/counter'import { asyncInc } from '../store/actions' 分別表示從 counteractions 導出所需的 action

我們重點看看 從 wepy-redux 中 引入的 connect 腻要,這個 connect 很關鍵复罐,它是連接 組件 和 狀態(tài) 的橋梁,主要用法是 @connect(states, actions) ~

  • states: 訪問 state 上的值雄家,可以是數(shù)組或者對象效诅,如果是對象的話,則包含的是 K-V 對趟济,V 可以是函數(shù)還可以是字符串乱投,如果是字符串的話則默認獲取 state[V], 否則的話則是使用返回值顷编;而對于如果是數(shù)組的話(數(shù)組中的項只能為字符串)戚炫,則認為是相同的 K-V 對象結構。states 最終會附加到組件的 computed 屬性值上媳纬。

  • actions: 只能傳入對象双肤,對象的 K-V 結構,如果 V 是字符串的話钮惠,則直接會 distatch 如下的結構:

    // args 就是調用傳入?yún)?shù)
    {
        type: val,
        // 修正一般情況下的參數(shù) 一般支持只傳一個參數(shù)
        // 如果真的是多個參數(shù)的話 那么 payload 就是參數(shù)組成的數(shù)組
        payload: args.length > 1 ? args : args[0]
    }
    

    如果是一個函數(shù) fn茅糜,則會 dispatch(val.apply(store, args)),否則的話則直接 dispatch(V)

這里萌腿,我們定義的 加一 限匣、 減一異步加一 操作直接映射到 INCREMENTDECREMENTasyncInc 上米死,也就是相當于直接 dispacth 對應的操作锌历,對數(shù)據(jù)進行變更~

現(xiàn)在效果應該可以看到了吧~

當然,我們也可以手動調用容器的 dispatch 方法對數(shù)據(jù)進行修改峦筒,我們的添加刪除 就是這么做的究西,
點擊添加按鈕,我們直接 dispatch 列表中的 ADD action物喷,如下:

wepy.$store.dispatch({ type : 'ADD' , payload : {
    title : '學習' ,
    text : '好好學習'
}})

刪除某一項卤材,只需 dispatch 列表的 REMOVE action ,傳入要刪除的索引即可 :

delete(index){
    wepy.$store.dispatch({ type : 'REMOVE' , payload : index })
},

不信你看~

大功告成~

結語

ok峦失,到現(xiàn)在我們也算是摸索著搞出來了一點名堂扇丛,回頭來看發(fā)現(xiàn)其實也并沒有那么困難吧,有學過 React 的同學應該對此不陌生尉辑,學起來光速吧~ 不過對于我來說帆精,我確實是屬于初探,希望能給跟我一樣萌新的小伙伴一個拋磚引玉的作用隧魄,如果有哪里寫的不對的地方卓练,還請批評斧正~

代碼我已經(jīng)托管到 github上,有需要的小伙伴自行下載查閱~

ps:wepy真的有很多坑~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末购啄,一起剝皮案震驚了整個濱河市襟企,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狮含,老刑警劉巖顽悼,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辉川,居然都是意外死亡表蝙,警方通過查閱死者的電腦和手機拴测,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門乓旗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人集索,你說我怎么就攤上這事屿愚。” “怎么了务荆?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵妆距,是天一觀的道長。 經(jīng)常有香客問我函匕,道長娱据,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任盅惜,我火速辦了婚禮中剩,結果婚禮上忌穿,老公的妹妹穿的比我還像新娘。我一直安慰自己结啼,他們只是感情好掠剑,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著郊愧,像睡著了一般朴译。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上属铁,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天眠寿,我揣著相機與錄音,去河邊找鬼焦蘑。 笑死澜公,一個胖子當著我的面吹牛,可吹牛的內容都是我干的喇肋。 我是一名探鬼主播坟乾,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蝶防!你這毒婦竟也來了甚侣?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤间学,失蹤者是張志新(化名)和其女友劉穎殷费,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體低葫,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡详羡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘿悬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实柠。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖善涨,靈堂內的尸體忽然破棺而出窒盐,到底是詐尸還是另有隱情,我是刑警寧澤钢拧,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布蟹漓,位于F島的核電站,受9級特大地震影響源内,放射性物質發(fā)生泄漏葡粒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗽交。 院中可真熱鬧伯铣,春花似錦、人聲如沸轮纫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掌唾。三九已至放前,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糯彬,已是汗流浹背凭语。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撩扒,地道東北人似扔。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像搓谆,于是被迫代替她去往敵國和親炒辉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內容