项目中redux的样板文件太分散,书写和维护都比较麻烦react
使用thunk来处理异步操做,不是那么直观git
基于redux数据流的管理方案:Dva
、mirror
和rematch
github
Dva是蚂蚁金服开源的一个数据流管理方案,基于redux和redux-saga,简化了开发体验。Dva是一揽子的解决方案,可使用侵入性很强的dva-cli来快速搭建项目,提供了路由层面的适配;也可使用dva-core来引入核心的代码,减小侵入性。json
若是使用Dva的一整套框架,现有的项目会有较大的改动redux
Dva使用redux-saga来处理异步,学习成本比较高promise
mirrorbash
相似于Dva的一个redux数据流方案,最新一次更新在两个月以前,一直没有发布1.0的版本react-router
rematch框架
rematch的灵感来自于Dva和mirror,将二者的有点结合了起来。less
使用了相似Dva的model文件结构,统一管理同步和异步操做
经过中间键实现了async/await的方式来处理异步,舍弃了Dva中的redux-saga
提供了redux的配置项,能够兼容项目中的老代码
支持多个store
将model中reducers和effects的方法挂载在dispatch函数上,形成dispatch既是一个函数,又是一个对象
Rematch | Mirror | Dva | |
---|---|---|---|
适用框架 | 全部框架 / 不使用框架 | React | React |
适用路由 | 全部路由 / 不使用路由 | RR4 | RR3, RR4 / 不使用路由 |
移动端 | √ | × | √ |
开发者工具 | Redux, Reactotron | Redux | Redux |
插件化 | √ | √ | √ |
reducers | √ | √ | √ |
effects | async/await | async/await | redux saga |
effect params | (payload, internals) | (action, state) | (action, state) |
监听方式 | subscriptions | hooks | subscriptions |
懒加载模型 | √ | √ | √ |
链式 dispatch | √ | √ | √ |
直接 dispatch | √ | ||
dispatch promises | √ | √ | |
加载插件 | √ | √ | √ |
persist plugin | √ | ||
package size | 14.9k(gzipped: 5.1k)|| redux + thunk: 6k(2k) | 130.4k(gzipped: 33.8k) | dva-core: 72.6k(gzipped: 22.5k) |
import { init } from '@rematch/core';
const store = init({
models: {
count: {
state: 0,
reducers: {
add: (state, payload) => state + payload,
del: (state, payload) => state - payload,
'otherModel/actionName': (state, payload) => state + payload,
},
effets: {
async loadData(payload, rootState) {
const response = await fetch('http://example.com/data')
const data = await response.json()
this.add(data)
}
}
}
list: {}
},
redux: {
reducers: {},
middlewares: [thunk],
},
plugins: [loading]
})复制代码
对rematch进行初始化,返回一个store对象,包含了使用redux初始化store对象的全部字段。
一个对象,属性的键做为rootState上的的键
用来初始化model
一个对象,属性是用来改变model state的方法,第一个参数是这个model的上一个state,第二个参数是payload,函数返回model下一个state。这些方法应该是纯函数。
一个对象,异步或者非纯函数的方法放在这个对象中,能够与async/await一块儿使用
经过这个属性,能够兼容老项目中的redux配置。
rematch是一个插件系统,经过这个字段能够配置第三方的插件。
redux流程:
rematch流程:
##index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { init } from '@rematch/core'
import App from './App'
const count = {
state: 0,
reducers: {
increment: s => s + 1,
},
effects: dispatch => ({
async asyncIncrement() {
await new Promise(resolve => {
setTimeout(resolve, 1000)
})
dispatch.count.increment()
},
}),
}
const store = init({
count,
})
// Use react-redux's <Provider /> and pass it the store. ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) ##App.js import React from 'react' import { connect } from 'react-redux' // Make a presentational component. // It knows nothing about redux or rematch. const App = ({ count, asyncIncrement, increment }) => ( <div> <h2> count is <b style={{ backgroundColor: '#ccc' }}>{count}</b>
</h2>
<h2>
<button onClick={increment}>Increment count</button>{' '}
<em style={{ backgroundColor: 'yellow' }}>(normal dispatch)</em>
</h2>
<h2>
<button onClick={asyncIncrement}>
Increment count (delayed 1 second)
</button>{' '}
<em style={{ backgroundColor: 'yellow' }}>(an async effect!!!)</em>
</h2>
</div>
)
const mapState = state => ({
count: state.count,
})
const mapDispatch = dispatch => ({
increment: dispatch.count.increment,
asyncIncrement: dispatch.count.asyncIncrement,
})
// Use react-redux's connect export default connect( mapState, mapDispatch )(App) 复制代码
主要针对已经使用thunk中间键的老项目。
一、安装依赖,并删除依赖中的redux
yarn add @rematch/core
yarn remove redux (删除redux可能会形成eslint报错)
二、修改redux入口文件
src/store/index.js
import { init } from '@rematch/core';
import thunk from 'redux-thunk';
import reduxReducerConfig from '@/reducers';
import models from '../models';
const store = init({
models,
redux: {
reducers: {
...reduxReducerConfig
},
middlewares: [thunk],
},
});
export default store;复制代码
三、修改reducers的入口文件
import { routerReducer as routing } from 'react-router-redux';
- import { combineReducers } from 'redux';
import dispatchConfigReducer from './dispatch-config';
import counterReducer from './count';
- export default combineReducers({
- routing,
- dispatchConfigReducer,
- counterReducer,
- });
+ export default {
+ routing,
+ dispatchConfigReducer,
+ counterReducer,
+ };复制代码
四、增长model的入口文件
+ src/models
+ src/models/re-count.js
+ src/models/config-list.js
+ src/models/index.js
index.js
import reCount from './re-count';
import configList from './config-list';
export default {
reCount,
configList,
};复制代码
若是老项目中没有使用redux,可使用yarn remove thunk
删除thunk的依赖和reducers这个文件夹,而且在init初始化的时候能够不用传redux这个配置。
若是接入rematch,须要锁定版本,rematch中引入的redux版本为4.0.0,因此老项目中的redux要更新为为4.0.0,否则打包的时候会把两个版本的redux都打进去。
index.js
import React from 'react';
import { render } from 'react-dom';
import { browserHistory, Router } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { Provider } from 'react-redux';
import routes from '@/routes';
import store from '@/store';
import '@/styles/index.less';
const history = syncHistoryWithStore(browserHistory, store);
render(
<Provider store={store}>
<Router history={history} routes={routes} />
</Provider>,
document.getElementById('root'),
);
---------------------------------------------------------------------------------------
新建store文件夹,并添加index.js
import { init } from '@rematch/core';
import { routerReducer as routing } from 'react-router-redux';
import models from '../models';
const store = init({
models,
redux: {
reducers: {
routing,
},
},
});
export default store;
---------------------------------------------------------------------------------------
新建models文件夹,并添加index
models结构
├── common
│ ├── bizLineList.js
│ └── index.js
└── index.js复制代码
Redux DevTools 要升级到最新版,2.16.0有bug