前陣子一直在作小程序開發,採用的是官方給的框架 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
reducers
和 actions
,其中 types
用於定義咱們要觸發的 action
的名稱,也就是表示 action
的名稱,這裏我定義了 counter
和 list
兩個 types
,內容分別以下:promise
counter.js
bash
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
})
複製代碼
首先將 counter
和 list
的分別引入進來,經過 redux
定義的 combineReducers
函數,將全部的 reducers
合併成一個總體,方便咱們後面對其進行管理!
那麼 counter
和 list
對應的 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
, 分別定義了 INCREMENT
、 DECREMENT
和 ASYNC_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
導出便可~
這個裏面我只定義了 counter
的 action
, 也就是爲了剛纔異步數據 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'
分別表示從 counter
和 actions
導出所需的 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)
這裏,咱們定義的 加一 、 減一 和 異步加一
操做直接映射到 INCREMENT
、DECREMENT
、asyncInc
上,也就是至關於直接 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
真的有不少坑~