前阵子一直在做小程序开发,采用的是官方给的框架 wepy , 如果还不了解的同学可以去他的官网查阅相关资料学习;不得不说的是,这个框架确相比于传统小程序开发模式确实方便很多,它的语法 Vue 的语法很像,可以实现组件化开发,方面后面代码的调整和维护...但是!!这个框架的坑也不是一点点,开发的时候总会遇到奇奇怪怪的问题,自己去踩吧,这样你才能进步~~ 废话了这么多,咳咳,上面的都不是我们要讨论的重点,我们今天的重点是—在小程序里使用 Redux 进行状态管理, Redux 是一个前端状态管理的容器,对于构建大型应用,对里面共享数据、状态的管理非常方便,学过 React 的同学对它应该不陌生,如果还不了解的同学,不如进服瞧一瞧; wepy 框架本身是支持 Redux 的,我们在构建项目的时候,将 是否安装 Redux 选择 y 就好了,会自动安装依赖,运行项目后看官方给的 demo 确实是可以做到的,但是官方文档里却对这一块只字不提,经过我自己尝试了一波,这才稍微摸清了它的使用方式,赶紧拿来与你们分享~ 注意了,接下来划重点了~ 具体实现 运行我们的项目,发现官网已经给了我们一些 Redux 的使用方法,实际上主要是放在 store 文件夹下面了,我们现在来一探究竟~ step1 入口文件 index.js ,里面主要是 初始化 Redux , 其中 promiseMiddleware 是一个中间件,方便后面 action 做异步处理~ reducers 是一个纯函数,用于接受 Action 和当前 State 作为参数,返回一个新的 State ~ <span style="color:#444444"><span style="color:#333333"><strong>import</strong></span> { createStore , applyMiddleware } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'redux'</span>
<span style="color:#333333"><strong>import</strong></span> promiseMiddleware <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'redux-promise'</span>
<span style="color:#333333"><strong>import</strong></span> reducer <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./reducers'</span>
<span style="color:#333333"><strong>const</strong></span> Store = createStore(
reducer ,
applyMiddleware(promiseMiddleware)
)
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>default</strong></span> configStore => Store</span> step2 剩下三个文件夹分别是 types reducers 和 actions ,其中 types 用于定义我们要触发的 action 的名称,也就是表示 action 的名称,这里我定义了 counter 和 list 两个 types ,内容分别如下: counter.js <span style="color:#444444"><span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> INCREMENT = <span style="color:#880000">'INCREMENT'</span>
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> DECREMENT = <span style="color:#880000">'DECREMENT'</span>
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> ASYNC_INCREMENT = <span style="color:#880000">'ASYNC_INCREMENT'</span></span> list.js <span style="color:#444444"><span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> ADD = <span style="color:#880000">'ADD'</span>
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> REMOVE = <span style="color:#880000">'REMOVE'</span></span> 最后通过 types 文件夹的入口文件 index.js 将他们暴露出去~ <span style="color:#444444"><span style="color:#333333"><strong>export</strong></span> * <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./counter'</span>
<span style="color:#333333"><strong>export</strong></span> * <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./list'</span></span> step3 reducers 文件件存放我们的纯函数,用来更改我们的状态 , 他也有一个入口文件 index.js ,定义如下: <span style="color:#444444"><span style="color:#333333"><strong>import</strong></span> { combineReducers } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'redux'</span>
<span style="color:#333333"><strong>import</strong></span> counter <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./counter'</span>
<span style="color:#333333"><strong>import</strong></span> list <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./list'</span>
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>default</strong></span> combineReducers({
counter ,
list
})</span> 首先将 counter 和 list 的分别引入进来,通过 redux 定义的 combineReducers 函数,将所有的 reducers 合并成一个整体,方便我们后面对其进行管理! 那么 counter 和 list 对应的 reducer 分别是 什么样的?我们直接看代码: counter.js <span style="color:#444444"><span style="color:#333333"><strong>import</strong></span> { handleActions } from <span style="color:#880000">'redux-actions'</span>
<span style="color:#333333"><strong>import</strong></span> { INCREMENT , DECREMENT , ASYNC_INCREMENT } from <span style="color:#880000">'../types/counter'</span>
const defaultState = {
num: <span style="color:#880000">0</span> ,
asyncNum: <span style="color:#880000">0</span>
}
export <span style="color:#333333"><strong>default</strong></span> handleActions({
[INCREMENT](state){
return{
...state,
num : state.num + <span style="color:#880000">1</span>
}
},
[DECREMENT](state){
return{
...state,
num : state.num - <span style="color:#880000">1</span>
}
},
[ASYNC_INCREMENT](state, action){
return {
...state ,
asyncNum : state.asyncNum + action.payload
}
}
},defaultState)</span> 我们介绍一下 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 <span style="color:#444444"><span style="color:#333333"><strong>import</strong></span> { handleActions } from <span style="color:#880000">'redux-actions'</span>
<span style="color:#333333"><strong>import</strong></span> { ADD , REMOVE } from <span style="color:#880000">'../types/list'</span>
const defaultState = [
{
title : <span style="color:#880000">'吃饭'</span> ,
text : <span style="color:#880000">'今天我要吃火锅'</span>
},
{
title : <span style="color:#880000">'工作'</span> ,
text : <span style="color:#880000">'今天我要学习Redux'</span>
}
]
export <span style="color:#333333"><strong>default</strong></span> handleActions({
[ADD]( state , action ){
state.push(action.payload)
return [...state]
},
[REMOVE]( state , action ){
state.splice( action.payload , <span style="color:#880000">1</span> );
return [ ...state ]
}
},defaultState)</span> step4 我们终于走到这一步了,到这里,你已经离预期不远啦,就剩一个 actions 文件件了,毫不例外,入口文件 index.js 如下: index.js <span style="color:#444444"><span style="color:#333333"><strong>export</strong></span> * <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'./counter'</span></span> 很简单,只需要将所需的 action 导出即可~ 这个里面我只定义了 counter 的 action , 也就是为了刚才异步数据 asyncNum 准备的~ counter.js <span style="color:#444444"><span style="color:#333333"><strong>import</strong></span> { ASYNC_INCREMENT } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'../types/counter'</span>
<span style="color:#333333"><strong>import</strong></span> { createAction } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'redux-actions'</span>
<span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>const</strong></span> asyncInc = createAction(ASYNC_INCREMENT,()=>{
<span style="color:#333333"><strong>return</strong></span> <span style="color:#333333"><strong>new</strong></span> <span style="color:#397300">Promise</span>(resolve=>{
setTimeout(()=>{
resolve(<span style="color:#880000">1</span>)
},<span style="color:#880000">1000</span>)
})
})</span> 这里跟 reducer 里面的要区分,这里是可以对数据进行一系列处理的,我们通过 createAction 创建一个 action , 该方法主要有两个参数,第一个参数 type 表示 action 的类型,第二个参数 payloadCreator 是一个 function ,处理并返回需要的 payload ;如果空缺,会使用默认方法。这里我们是延迟 1s 后返回一个 1 ; ok,到此为止,你已经基本完成了一个 redux 的容器~ 接下来,就是展示它怎么使用的时候了~ step5 我们创建一个 index.wpy 的文件,这里我把代码直接贴出来,然后慢慢来分析看看~ 代码如下: <span style="color:#444444"><<span style="color:#333333"><strong>template</strong></span> lang=<span style="color:#880000">"wxml"</span>> <<span style="color:#333333"><strong>view</strong></span> class=<span style="color:#880000">"container"</span>> <<span style="color:#333333"><strong>text</strong></span>>同步<span style="color:#bc6060">{{ num }}</span></<span style="color:#333333"><strong>text</strong></span>> <<span style="color:#333333"><strong>text</strong></span>>异步<span style="color:#bc6060">{{ asyncNum }}</span></<span style="color:#333333"><strong>text</strong></span>> <<span style="color:#333333"><strong>button</strong></span> @tap=<span style="color:#880000">"increment"</span> type=<span style="color:#880000">"primary"</span>>加一</<span style="color:#333333"><strong>button</strong></span>> <<span style="color:#333333"><strong>button</strong></span> @tap=<span style="color:#880000">"decrement"</span> type=<span style="color:#880000">"primary"</span>>减一</<span style="color:#333333"><strong>button</strong></span>> <<span style="color:#333333"><strong>button</strong></span> @tap=<span style="color:#880000">"asyncIncrement"</span> type=<span style="color:#880000">"primary"</span>>异步加一</<span style="color:#333333"><strong>button</strong></span>> <<span style="color:#333333"><strong>button</strong></span> @tap=<span style="color:#880000">"addList"</span>>添加</<span style="color:#333333"><strong>button</strong></span>> <<span style="color:#333333"><strong>view</strong></span> class=<span style="color:#880000">"box"</span>> <<span style="color:#333333"><strong>view</strong></span> class=<span style="color:#880000">"item"</span> wx:for-items=<span style="color:#880000">"</span><span style="color:#bc6060">{{ todoList }}</span><span style="color:#880000">"</span> wx:key=<span style="color:#880000">"index"</span>> <<span style="color:#333333"><strong>view</strong></span> class=<span style="color:#880000">"title"</span>><span style="color:#bc6060">{{ item.title }}</span></<span style="color:#333333"><strong>view</strong></span>> <<span style="color:#333333"><strong>view</strong></span> class=<span style="color:#880000">"content"</span>><span style="color:#bc6060">{{ item.text }}</span></<span style="color:#333333"><strong>view</strong></span>> <<span style="color:#333333"><strong>button</strong></span> type=<span style="color:#880000">"primary"</span> class=<span style="color:#880000">"delete"</span> @tap=<span style="color:#880000">"delete(</span><span style="color:#bc6060">{{index}}</span><span style="color:#880000">)"</span>>删除</<span style="color:#333333"><strong>button</strong></span>> </<span style="color:#333333"><strong>view</strong></span>> </<span style="color:#333333"><strong>view</strong></span>> </<span style="color:#333333"><strong>view</strong></span>> </<span style="color:#333333"><strong>template</strong></span>> <<span style="color:#333333"><strong>script</strong></span>> <span style="color:#333333"><strong>import</strong></span> wepy <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'wepy'</span> <span style="color:#333333"><strong>import</strong></span> { connect } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'wepy-redux'</span> <span style="color:#333333"><strong>import</strong></span> { INCREMENT , DECREMENT } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'../store/types/counter'</span> <span style="color:#333333"><strong>import</strong></span> { asyncInc } <span style="color:#333333"><strong>from</strong></span> <span style="color:#880000">'../store/actions'</span> @connect({ num(state){ <span style="color:#333333"><strong>return</strong></span> state.counter.num; }, asyncNum(state){ <span style="color:#333333"><strong>return</strong></span> state.counter.asyncNum; } },{ increment : INCREMENT , decrement : DECREMENT , asyncIncrement : asyncInc }) <span style="color:#333333"><strong>export</strong></span> <span style="color:#333333"><strong>default</strong></span> <span style="color:#333333"><strong>class</strong></span> <span style="color:#880000"><strong>Index</strong></span> <span style="color:#333333"><strong>extends</strong></span> <span style="color:#880000"><strong>wepy</strong></span>.<span style="color:#880000"><strong>page</strong></span> { components = {} computed = { todoList(){ <span style="color:#333333"><strong>return</strong></span> wepy.$store.getState().list; } } methods = { <span style="color:#333333"><strong>delete</strong></span>(index){ wepy.$store.dispatch({ type : <span style="color:#880000">'REMOVE'</span> , payload : index }) }, addList(){ wepy.$store.dispatch({ type : <span style="color:#880000">'ADD'</span> , payload : { title : <span style="color:#880000">'学习'</span> , text : <span style="color:#880000">'好好学习'</span> }}) } } onLoad () { <span style="color:#397300">console</span>.log(wepy.$store.getState()) } } </<span style="color:#333333"><strong>script</strong></span>> <<span style="color:#333333"><strong>style</strong></span> lang=<span style="color:#880000">"less"</span>> 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; } } </<span style="color:#333333"><strong>style</strong></span>></span> 不出意外,运行后,你的小程序的界面会跟下面一样————丑~ 点一点看,发现卧槽,很牛逼,有木有~ ok~ 我们一起看看上面的代码是怎么做的~ 样式结构方面我们这里不做讨论,主要看 js 部分,其中 import { INCREMENT , DECREMENT } from '../store/types/counter' 和 import { asyncInc } from '../store/actions' 分别表示从 counter 和 actions 导出所需的 action 我们重点看看 从 wepy-redux 中 引入的 connect ,这个 connect 很关键,它是连接 组件 和 状态 的桥梁,主要用法是 @connect(states, actions) ~
|