手把手教你在小程序里使用 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真的有不少坑~

相关文章
相关标签/搜索