在小程序中使用redux

手把手教你在微信小程序使用Redux

来自知乎 凌空

首先,为什么要在微信小程序中使用Redux?小程序虽然主张轻量级应用,但也难免会有些复杂情况。在应对某些异步场景,以及多个打开着的页面相互影响的情况下,你需要有一个数据流管理库才能更好地缕清思路,增加项目的可维护性。

举个栗子:你在A页面点一个按钮会发送远程请求,同时转跳到B页面,远程请求返回值在B页面显示。这个场景在B页面加载时远程请求并没有结束,此请求会在获得返回值的同时响应到B页面上。

因为在A页面点按钮时并不存在B页面,所以无法在远程请求的异步回调中用getCurrentPages()在B页面setData。如果用app对象下的全局变量周转,手写代码监听变化,在项目变大后会变得极其难维护。而使用Redux就可以很好地适应这种场景。

下面我针对刚举的例子一步一步来说说如何将Redux整合进小程序,在文章结尾有GitHub链接。先创一个项目,有没有appId都行。

image

在创建完后会有一个默认的小项目,我们就基于这个来吧。获取redux.min.js并放进目录。可以找cdn下载到本地,也可以从redux官方repo里自己编译,只有10kb左右。然后创建actions.js和reducers.js,目录如下图。

image

添加一个修改文字的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>

image

修改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'字样

image

这就是一个最基本的在微信小程序使用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文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。