手把手教你在微信小程序使用Redux
来自知乎 凌空
首先,为什么要在微信小程序中使用Redux?小程序虽然主张轻量级应用,但也难免会有些复杂情况。在应对某些异步场景,以及多个打开着的页面相互影响的情况下,你需要有一个数据流管理库才能更好地缕清思路,增加项目的可维护性。
举个栗子:你在A页面点一个按钮会发送远程请求,同时转跳到B页面,远程请求返回值在B页面显示。这个场景在B页面加载时远程请求并没有结束,此请求会在获得返回值的同时响应到B页面上。
因为在A页面点按钮时并不存在B页面,所以无法在远程请求的异步回调中用getCurrentPages()在B页面setData。如果用app对象下的全局变量周转,手写代码监听变化,在项目变大后会变得极其难维护。而使用Redux就可以很好地适应这种场景。
下面我针对刚举的例子一步一步来说说如何将Redux整合进小程序,在文章结尾有GitHub链接。先创一个项目,有没有appId都行。
在创建完后会有一个默认的小项目,我们就基于这个来吧。获取redux.min.js并放进目录。可以找cdn下载到本地,也可以从redux官方repo里自己编译,只有10kb左右。然后创建actions.js和reducers.js,目录如下图。
添加一个修改文字的action,以及一个reducer用来改变Store里文字的状态
// actions.js
export const changeText = (text) => {
return { type: 'CHANGE_TEXT', text }
}
// reducers.js
export default {
myText(state = '', action) {
switch (action.type) {
case 'CHANGE_TEXT':
return action.text
default:
return state
}
}
}
app.js中初始化Store,并放在App实例里以方便调用
//app.js
import { createStore, combineReducers } from './lib/redux.min'
import reducers from './reducers'
const Store = createStore(combineReducers(reducers))
App({
Store,
onLaunch: function () {
// ...
在首页index.wxml编辑下,添加一个按钮,并bindtap
<!--index.wxml-->
<view class="container">
<view bindtap="bindViewTap" class="userinfo">
<image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</view>
<view class="usermotto">
<button type="primary" bindtap="bindBtn">2秒后改变下一页的文字</button>
</view>
</view>
修改index.js,在bindBtn里用setTimeout 2000的方式模拟异步远程请求2秒后返回,再dispatch action将下一页的文字设为'new text'。同时转跳到下一页。
//index.js
import { changeText } from '../../actions'
//获取应用实例
const app = getApp()
const Store = app.Store
const dispatch = Store.dispatch
Page({
data: {
userInfo: {}
},
bindBtn() {
setTimeout(() => {
dispatch(changeText('new text'))
}, 2000)
wx.navigateTo({
url: '../logs/logs',
})
},
// ...
在logs.wxml里只放一个{{foo}}
<!--logs.wxml-->
<view class="container">
{{foo}}
</view>
这一步是重点,在logs.js里的onLoad订阅Store并响应式刷新data,在onUnload里取消订阅以节省资源。生命周期的管理看需要,也可以在onShow里订阅,onHide里取消订阅。
//logs.js
const app = getApp()
const Store = app.Store
const dispatch = Store.dispatch
Page({
data: {
foo: ''
},
onLoad() {
const updateDate = () => {
const foo = Store.getState().myText
this.setData({ foo })
}
updateDate();
this.unsubStore = Store.subscribe(() => updateDate())
},
onUnload() {
this.unsubStore()
}
})
我们可以看到,logs页面foo变量的初始值是空String,当Redux Store里的myText变化后马上触发subscribe的回调,并用setData设置本页面变量。
我们来测试下,在首页点击绿色按钮后,瞬间转跳到下一页,过两秒后出现'new text'字样
这就是一个最基本的在微信小程序使用Redux的方法了。更高级的用法比如再配合redux-thunk这样的中间件使用,让异步流程更清晰。
GitHub Repo地址 https://github.com/lhz516/wechat-mina-redux-example
本文例子是用Redux底层API实现,另外还可以用高阶函数绑定的方式实现,比如这个库 wechat-weapp-redux ,它是参照react-redux中Provider和connect的概念。但我还是推荐初学者用Redux底层API走一遍,因为目前在小程序用Redux高阶函数绑定的思路还不够成熟,比如是否只在onLoad里subscribe store,在onUnload里unsubscribe store?如果想使用,得做好自己维护的准备。
相关传送门:Redux文档