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

微信小程序裏使用 Redux 狀態管理

前言

前陣子一直在作小程序開發,採用的是官方給的框架 wepy , 若是還不瞭解的同窗能夠去他的官網查閱相關資料學習;不得不說的是,這個框架確相比於傳統小程序開發模式確實方便不少,它的語法 Vue 的語法很像,能夠實現組件化開發,方面後面代碼的調整和維護...可是!!這個框架的坑也不是一點點,開發的時候總會遇到奇奇怪怪的問題,本身去踩吧,這樣你才能進步~~html

廢話了這麼多,咳咳,上面的都不是咱們要討論的重點,咱們今天的重點是—在小程序裏使用 Redux 進行狀態管理,Redux 是一個前端狀態管理的容器,對於構建大型應用,對裏面共享數據、狀態的管理很是方便,學過 React 的同窗對它應該不陌生,若是還不瞭解的同窗,不如進服瞧一瞧;前端

wepy 框架自己是支持 Redux 的,咱們在構建項目的時候,將 是否安裝 Redux 選擇 y 就行了,會自動安裝依賴,運行項目後看官方給的demo確實是能夠作到的,可是官方文檔裏卻對這一塊隻字不提,通過我本身嘗試了一波,這才稍微摸清了它的使用方式,趕忙拿來與大家分享~git

注意了,接下來劃重點了~github

具體實現

運行咱們的項目,發現官網已經給了咱們一些 Redux 的使用方法,實際上主要是放在 store 文件夾下面了,咱們如今來一探究竟~redux

step1小程序

入口文件 index.js ,裏面主要是 初始化 Redux , 其中 promiseMiddleware 是一箇中間件,方便後面 action 作異步處理~ reducers 是一個純函數,用於接受 Action 和當前 State 做爲參數,返回一個新的 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 用於定義咱們要觸發的 action 的名稱,也就是表示 action的名稱,這裏我定義了 counterlist 兩個 types ,內容分別以下:promise

counter.jsbash

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文件件存放咱們的純函數,用來更改咱們的狀態 , 他也有一個入口文件 index.js,定義以下:

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

複製代碼

首先將 counterlist 的分別引入進來,經過 redux 定義的 combineReducers 函數,將全部的 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)
複製代碼

咱們介紹一下 counter.js 裏面的 reducer , 首先引入了 handleActions 方法用來建立 actions , 它將多個相關的 reducer 寫在一塊兒也是 ,方面後期維護,也方便後期經過 dispatch 來調用他們更改 state 裏面的狀態,它主要接收兩個參數,第一個參數時候個大對象,裏面存放多個 reducer , 第二個參數是初始化的時候 state 的狀態值,所以,咱們一開始就定義了 defaultState ;

接着,咱們看看裏面的 reducer , 分別定義了 INCREMENTDECREMENTASYNC_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

咱們終於走到這一步了,到這裏,你已經離預期不遠啦,就剩一個 actions 文件件了,絕不例外,入口文件 index.js 以下:

index.js

export * from './counter'
複製代碼

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

這個裏面我只定義了 counteraction , 也就是爲了剛纔異步數據 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 裏面的要區分,這裏是能夠對數據進行一系列處理的,咱們經過 createAction 建立一個 action , 該方法主要有兩個參數,第一個參數 type 表示 action 的類型,第二個參數 payloadCreator 是一個 function,處理並返回須要的 payload ;若是空缺,會使用默認方法。這裏咱們是延遲 1s 後返回一個 1

ok,到此爲止,你已經基本完成了一個 redux 的容器~

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

step5

咱們建立一個 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>

複製代碼

不出意外,運行後,你的小程序的界面會跟下面同樣————醜~

點一點看,發現臥槽,很牛逼,有木有~

ok~ 咱們一塊兒看看上面的代碼是怎麼作的~

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

咱們重點看看 從 wepy-redux 中 引入的 connect ,這個 connect 很關鍵,它是鏈接 組件 和 狀態 的橋樑,主要用法是 @connect(states, actions) ~

  • states: 訪問 state 上的值,能夠是數組或者對象,若是是對象的話,則包含的是 K-V 對,V 能夠是函數還能夠是字符串,若是是字符串的話則默認獲取 state[V], 不然的話則是使用返回值;而對於若是是數組的話(數組中的項只能爲字符串),則認爲是相同的 K-V 對象結構。states 最終會附加到組件的 computed 屬性值上。

  • actions: 只能傳入對象,對象的 K-V 結構,若是 V 是字符串的話,則直接會 distatch 以下的結構:

    // args 就是調用傳入參數
    {
        type: val,
        // 修正通常狀況下的參數 通常支持只傳一個參數
        // 若是真的是多個參數的話 那麼 payload 就是參數組成的數組
        payload: args.length > 1 ? args : args[0]
    }
    複製代碼

    若是是一個函數 fn,則會 dispatch(val.apply(store, args)),不然的話則直接 dispatch(V)

這裏,咱們定義的 加一減一異步加一 操做直接映射到 INCREMENTDECREMENTasyncInc 上,也就是至關於直接 dispacth 對應的操做,對數據進行變動~

如今效果應該能夠看到了吧~

固然,咱們也能夠手動調用容器的 dispatch 方法對數據進行修改,咱們的添加刪除 就是這麼作的, 點擊添加按鈕,咱們直接 dispatch 列表中的 ADD action,以下:

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

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

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

不信你看~

大功告成~

結語

ok,到如今咱們也算是摸索着搞出來了一點名堂,回頭來看發現其實也並無那麼困難吧,有學過 React 的同窗應該對此不陌生,學起來光速吧~ 不過對於我來講,我確實是屬於初探,但願能給跟我同樣萌新的小夥伴一個拋磚引玉的做用,若是有哪裏寫的不對的地方,還請批評斧正~

代碼我已經託管到 github上,有須要的小夥伴自行下載查閱~

ps:wepy真的有不少坑~

相關文章
相關標籤/搜索