-
完整 redux 支持(模块化、异步数据流流、强大的生态)🔥🔥
-
原生 page 对象结构的 connect,类似 react-redux(较低的学习成本、便捷的移植方案)🔥🔥
-
最小的 diff 更新 🔥🔥
- 为什么是 redux
redux 作为一个最好的 flux 最好的实现之一,他拥有庞大的社区支持与完善的插件支持。并且相对于 vuex 的定制化,redux 作为一个极简的状态管理系统(通过插件实现定制化),天生有易于移植、适用于任何框架的基因。事实上,本文介绍的minapp-redux
也可以归属于 redux 的定制化插件之一。
- 状态如何映射到视图
小程序框架没有类似 react 的 props 语法,并且由于桥接语法的存在,使得 JS 层只有 data 数据才能 map 到 wxml。所以minapp-redux
的 connect 语法会将第一参数(stateMapFunction)
返回的对象并入 page 对象定义的 data 中,使用是需要注意不能有属性重名,否则会有告警:stateMap 数据会覆盖 page 对象中定义的重名 data 属性。
- 运行时
由于小程序规范,data 中的数据必须在初始化 page 对象时存在,minapp-redux
提供的 connect 语法会在小程序初始化时调用,使用Page(connect(stateMapFun, methodMapFun)(pageObject))
语法,pageObject 保持了你原本 page 对象的完整性,便于项目移植或移除minapp-redux
。在后续 redux 状态改变的时候,redux api subscribe
监听状态变化,并通过保存 store 实例使用 setData 更新到视图。
- 最小的 diff 更新
minapp-redux
使用了专属于小程序的 diff 更新机制,在有属性变化的时候会比较新 store 返回的 stateMap 与 page 对象中当前 data 的属性,并且抽取出变化属性的集合(丢弃相同值的属性),保证最小粒度的更新。值得注意的一点是,有时候我们会使用特别复杂的 store 对象作为 map 属性,而对这些复杂对象的 diff 对比会带来比较大的开销,所以minapp-redux
使用浅比较,当遇到 map 数据类型为 Object 时,会将整个对象直接赋值更新。
- 完整的 Redux 支持
前面说过minapp-redux
只是作为一个 redux 的定制化插件存在,它没有改变任何的 redux 原有功能,任何你需要用到的无浏览器支持的 redux 插件你都可以无缝使用到你的minapp-redux
项目中,例如:redux-logger、redux-thunk 等等。
- 模块化支持
redux 本身没有提供模块化方法,minapp-redux 为了带来更好的使用体验,内置了 redux 的模块化封装,提供类似 vuex 开箱即用的模块化能力。
- npm 构建
npm install minapp-redux --save
- 直接引入
复制项目 src 文件夹下 index.js 到项目中
const { use, connect, connectComponent, createModule, combineModules } = require(' minapp-redux')
/**
* use
* @param {Object} Store
*/
/**
* connect
* @param {Function} mapStateToData
* @param {Function} mapMethodToPage
* @return {Function}
*/
/**
* connectComponent
* @param {Function} mapStateToData
* @param {Function} mapMethodToPage
* @return {Function}
*/
/**
* createModule
* 创建模块 模块对象必须为一个标准对象
* {
name: 'xxx',
initialState: {
},
asyncActions: actions => ({
xxx: (payload: any) {
return (dispatch, getState) => any
}
}),
reducers: {
xxx: (state, action) {
return { ...state, ...any}
}
}
}
* 返回一个redux模块
* @param {Object} module
* @returns {Object}
*/
/**
* combineModules
* 合并 modules,module 为使用 createModule 创建
* 合并后的 modules
* @param {Object} modules
* @returns {Object}
*/
// app.js
import { use } from 'minapp-redux'
// redux Store
import Store from '../../store/index'
//inject Store
use(Store)
App({
onLaunch() {},
})
// pages/login/index.js
import { connect } from '../../libs/minapp-redux'
import * as Actions from '../../store/userInfo/actions.js'
const stateMap = (state) => {
const { userInfo } = state
return {
hasLogin: userInfo.hasLogin,
userName: userInfo.userName,
}
}
const methodMap = (dispatch, state) => ({
login(userName) {
dispatch(
Actions.login({
hasLogin: true,
userName,
})
)
},
})
const page = {
data: {
username: '',
},
bindUserNameChange(e) {
this.setData({
username: e.detail.value,
})
},
bindLogin() {
if (!this.data.username) return
this.login(this.data.username)
wx.navigateBack({
delta: 1,
})
},
}
Page(connect(stateMap, methodMap)(page))
// store/modules/user.js
import { createModule } from '../../libs/minapp-redux'
export default createModule({
name: 'user',
initialState: {
userInfo: {}
},
actions: {
setUserInfo: v => v
},
asyncActions: actions => ({
getUserInfo(userName) {
return function(dispatch, getState) {
return Promise.resolve({ userName, id: 1 }).then(
res => {
dispatch(actions.setUserInfo(res))
return res
},
err => Promise.reject(err)
)
}
}
}),
reducers: {
setUserInfo(state, action) {
return { ...state, userInfo: action.payload }
}
}
})
// store/index.js
import { createStore, combineReducers, applyMiddleware } from '../libs/redux'
import thunk from '../libs/redux-thunk.js'
import logger from '../libs/redux-logger'
import { combineModules } from '../libs/minapp-redux'
import user from './user'
let modules = combineModules({
user
})
let middleware = [thunk, logger]
const Store = createStore(
combineReducers(modules.reducers),
applyMiddleware(...middleware)
)
export const actions = modules.actions
export default Store
更具体使用可查看demo