微信小程序里使用 Redux 状态管理
微信小程序里使用 Redux 状态管理
前言
前阵子一直在做小程序开发,采用的是官方给的框架 wepy
, 如果还不了解的同学可以去他的官网查阅相关资料学习;不得不说的是,这个框架确相比于传统小程序开发模式确实方便很多,它的语法 Vue 的语法很像,可以实现组件化开发,方面后面代码的调整和维护...但是!!这个框架的坑也不是一点点,开发的时候总会遇到奇奇怪怪的问题,自己去踩吧,这样你才能进步~~
废话了这么多,咳咳,上面的都不是我们要讨论的重点,我们今天的重点是—在小程序里使用 Redux
进行状态管理,Redux
是一个前端状态管理的容器,对于构建大型应用,对里面共享数据、状态的管理非常方便,学过 React
的同学对它应该不陌生,如果还不了解的同学,不如进服瞧一瞧;
wepy
框架本身是支持 Redux
的,我们在构建项目的时候,将 是否安装 Redux
选择 y
就好了,会自动安装依赖,运行项目后看官方给的demo
确实是可以做到的,但是官方文档里却对这一块只字不提,经过我自己尝试了一波,这才稍微摸清了它的使用方式,赶紧拿来与你们分享~
注意了,接下来划重点了~
具体实现
运行我们的项目,发现官网已经给了我们一些 Redux
的使用方法,实际上主要是放在 store
文件夹下面了,我们现在来一探究竟~
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
,内容分别如下:
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
文件件存放我们的纯函数,用来更改我们的状态 , 他也有一个入口文件 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
真的有很多坑~
作者:Chris威
链接:https://juejin.im/post/5b067f6ff265da0de02f3887
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
微信小程序里使用 Redux 状态管理的更多相关文章
- 在微信小程序中使用redux
本文主要讲述,在微信小程序中如何使用redux DEMO 需要解决的问题 如何在小程序中引入redux状态管理库,及它相关的插件? 微信小程序没有清晰的异步api,便于thunkMiddleware处 ...
- 微信小程序里如何用阿里云上传视频,图片。。
纯手写,踩了半天多的坑干出来了... 网上也有对于阿里云如何在微信小程序里使用,但是很不全,包括阿里云文档的最佳实践里. 话不多说上代码了. upvideo(){ var aliOssParams = ...
- 在微信小程序里使用 watch 和 computed
在开发 vue 的时候,我们可以使用 watch 和 computed 很方便的检测数据的变化,从而做出相应的改变,但是在小程序里,只能在数据改变时手动触发 this.setData(),那么如何给小 ...
- 微信小程序里实现跑马灯效果
在微信小程序 里实现跑马灯效果,类似滚动字幕或者滚动广告之类的,使用简单的CSS样式控制,没用到JS wxml: <!-- 复制的跑马灯效果 --> <view class=&quo ...
- 微信小程序里碰到的坑和小知识
本文作者:dongtao 来自:授权地址 本人低级程序员,以下bug不能确保在其它地方可以以相同的原因复现.同时, 出现很多bug的原因是小程序的基本知识还有编码的基本功不到位造成 路还很长,共勉 ...
- 微信小程序里的bug---video 的play()
微信小程序hidden转换后执行play()用真机测试不会播放.在调试器里可以. 解决方法,把hidden换成wx:if. 我刚开始以为网速问题,其实不是, 具体我也不知道为什,换上wxif解决了.
- 微信小程序里使用阿里巴巴矢量图标
登录 阿里巴巴矢量图标 (https://www.iconfont.cn) 选中图标,加入购物车图标 下载源代码 解析出来如下文件结构 有两种使用方式: 1)不转换成base64的文件 找到 icon ...
- 手持式停车收费管理系统全套案例,支持车牌识别,包含了android版app,微信小程序查询,响应式管理后台,云端大数据存储
先展示几个app效果图片吧,使用起来非常方便,关联了机器的快捷键操作,操作速度提高了不少,摄像头车牌自动识别,车牌识别无网络情况下离线也可以使用 再来一张后台截图,停车场信息完整显示,今日数据实时 ...
- 在微信小程序里自动获得当前手机所在的经纬度并转换成地址
效果:我在手机上打开微信小程序,自动显示出我当前所在的地理位置: 具体步骤: 1. 使用微信jssdk提供的getLocation API拿到经纬度: 2. 调用高德地图的api使用经纬度去换取地址的 ...
随机推荐
- Serialization之BinaryFormatter
前言 BinaryFormatter序列化二进制序列化使用二进制编码来生成精简的序列化,以用于存储或基于套接字的网络流等. 内容 下面通过一个小小的例子来给大家说明什么是BinaryFormatter ...
- Python从小看到大
最近迷恋上了python,因为一个朋友说python这种脚本语言很厉害,可以做网络攻防的时候用,但是由于自己太笨了,不得不从基础教程学起. 行左右.你可能会问为什么这么少的代码量,这门语言没有火起来, ...
- centos-7.4_ceph-12.2.4部署
centos-7.4_ceph-12.2.4部署: 前言: 基于centos7.4安装ceph-luminous的主要步骤有一下几点: 1.安装centos7.4的系统,并配置网卡 2.安装前的环境配 ...
- P1556 幸福的路
题意:平面内有N头牛$N\le 10$john从(0,0)出发,最后回到(0,0) 只有走到牛那里john才可以改变方向,否则沿着直线走 问john经过每一头牛并且在每一头牛出恰好改变方向一次的方案( ...
- kuangbin专题十六 KMP&&扩展KMP HDU3746 Cyclic Nacklace
CC always becomes very depressed at the end of this month, he has checked his credit card yesterday, ...
- Python字符串拼接、格式化输出、深浅复制
1.Python字符串拼接:方法挺多.挺好用的.灵活使用可使代码简洁.可读性好. #1.用4种方法,将列表li = ['I','python','like'], #里面的单词拼成: I**like** ...
- pandas学习3(数据处理)
- jupyter notebook自动补全功能实现
Jupyter notebook使用默认的自动补全是关掉的.要打开自动补全,需修改默认配置. 命令行中输入:ipython profile create 以上命令会在~/.ipython/profil ...
- mapreduce统计总数
现有某电商网站用户对商品的收藏数据,记录了用户收藏的商品id以及收藏日期,名为buyer_favorite1. buyer_favorite1包含:买家id,商品id,收藏日期这三个字段,数据以“\t ...
- PIXI 写一个字及图片保存(2)
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...