我们在学习
react
的路上不断前行,遇到了各式各样的框架及架构,但整体是要做一个兼容性及性能非常好的项目并非易事,在react
中,出现了redux
生态圈,得以将react
这匹黑马驾驭得更好,此文将演示redux
生态在实际项目运用。
整备
网络:axios
一个基于promise
实现的网络请求库,功能齐全,库精简
融合:redux-actions
解放switch
,代码更简洁
性能:redux-ignore、reselect
优化reduce
与渲染
数据流:redux-observable
可将复杂的业务逻辑异步化
持久化:redux-persist
将整个store
树持久化,解放各类保存数据
界面:material-ui
在追过艺术道路上的UI
布道者
结构
.
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── ico
│ │ ├── favicon.icns
│ │ ├── favicon.ico
│ │ └── favicon.png
│ └── index.html
├── src
│ ├── components
│ │ └── app.js
│ ├── config.json
│ ├── epics
│ │ ├── app.js
│ │ └── index.js
│ ├── index.js
│ ├── middles
│ │ ├── index.js
│ │ ├── log.js
│ │ └── warn.js
│ ├── reducers
│ │ ├── app.js
│ │ └── index.js
│ ├── rx
│ │ └── extends.js
│ ├── selectors
│ │ └── counter.js
│ └── store
│ └── persistor.js
└── yarn.lock
目录 | 说明 |
---|---|
src/components | 存放页面组件 |
epics | 异步请求或逻辑 |
middles | 各类中间件 |
reducers | redux core |
rx | rxjs 扩展方法 |
selectors | 渲染缓存或多redux共享数据 |
store | 整合专用 |
- 项目会从src/index.js入口开始加载
- redux的store也是从这里开始初始化
- src/store/persistor.js
这里的persistor我们配置非常简单,因为我们把一些配置交给了reducers
,middles
,epics
的index.js
去自动配置了
import {composeWithDevTools} from 'redux-devtools-extension';
# 1. 这里使用引入redux-devtools开发者工具,用于观察action与state树的信息
const epicMiddleware = createEpicMiddleware();
# 2. 实例化一个epic异常流框架
const rootEpic = combineEpics(
...epics
);
# 3. 接着把我们`src/epics`目录下的所有组件组合起来
let store = createStore(
persistedReducer,
preloadedState,
comp(applyMiddleware(epicMiddleware, ...middles))
);
# 4. redux的store初始化,包括各类中间件
epicMiddleware.run(rootEpic);
# 5. 别忘了运行异常流处理(之前版本不用手动流行,新版本必需)
- src/reducers/index.js
import {enableBatching} from 'redux-batched-actions';
# 这里引入批actions,可以同时发送多个action,实际业务中会用到很多
import {combineReducers} from "redux";
import appReducer from "./app";
export default enableBatching(combineReducers({
...appReducer,
}));
- src/reducers/app.js
import {createActions, handleActions} from 'redux-actions';
# 去除switch,简洁我们的代码,注重代码可视化
import {filterActions} from 'redux-ignore';
# 增加性能,属于自己的功能自己的处理
export const actions = createActions({
changeText: (text) => ({text}),
searchSuccess: (data) => ({data}),
searchFailed: (error) => ({error}),
});
export const types = {
changeText: 'changeText',
search: 'search',
searchSuccess: 'searchSuccess',
searchFailed: 'searchFailed',
};
const defaultState = {
name: '',
searching: false,
list: []
};
const reducer = handleActions({
changeText: (state, action) => ({...state, searching: true, name: action.payload.text}),
searchSuccess: function (state, action) {
return {...state, list: action.payload.data.items, searching: false};
},
searchFailed: function (state, action) {
return {...state, searching: false};
},
}, defaultState);
export default {
app: filterActions(reducer, Object.keys(actions))
};
- src/epics/app.js
import 'rxjs/Rx';
import '../rx/extends';
import axios from 'axios';
import {Observable} from 'rxjs/Rx';
import {types, actions} from "../reducers/app";
export const apis = {
searchApi:(name)=>axios.get(`https://api.github.com/search/users?q=${name}`)
};
const search = action$ =>
action$.ofType(types.changeText)
.debounceTime(1000) //去抖动
.switchMap(action => Observable.fromPromise(apis.searchApi(action.payload.text)))
.retryWithDelay(3, 2000)//网络重试
.map(response => actions.searchSuccess(response.data))
.catch(error => Observable.of(actions.searchFailed(error)));
export default [search];
- src/middles/index.js
import {batchDispatchMiddleware} from 'redux-batched-actions';
import warn from './warn';
import log from './log';
# 可以引入自己的开发中间件
export default [
batchDispatchMiddleware,
warn,
log
]
- src/selectors/counter.js
import {createSelector} from 'reselect';
# 可以用于共享多个redux的数据,在components组件中引入即可,带缓存功能,增加渲染性能
const todosSelector = (state, props) => state.yeah;
# 所有带计算功能的这将放入这里
const fSelector = createSelector(
[todosSelector],
(todos) => {
return {
count:Math.abs(1,2),//这里简单列出个别函数,一般用于大量判断条件,比对,复制类的
...todos
}
}
);
export default fSelector;
项目开发
npm start
演示效果
这里使用github的api搜索用户信息,在1秒内多次输入只会以最后一次为准,经典的去抖搜索方式
传送门 react-redux-template