React + Redux 架构设计

我们在学习react的路上不断前行,遇到了各式各样的框架及架构,但整体是要做一个兼容性及性能非常好的项目并非易事,在react中,出现了redux生态圈,得以将react这匹黑马驾驭得更好,此文将演示redux生态在实际项目运用。


整备

网络:axios
一个基于promise实现的网络请求库,功能齐全,库精简
融合:redux-actions
解放switch,代码更简洁
性能:redux-ignorereselect
优化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 整合专用
  1. 项目会从src/index.js入口开始加载
  2. redux的store也是从这里开始初始化
  3. src/store/persistor.js
    这里的persistor我们配置非常简单,因为我们把一些配置交给了reducers,middles,epicsindex.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. 别忘了运行异常流处理(之前版本不用手动流行,新版本必需)
  1. 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,
}));
  1. 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))
};
  1. 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];
  1. src/middles/index.js
import {batchDispatchMiddleware} from 'redux-batched-actions';
import warn from './warn';
import log from './log';
# 可以引入自己的开发中间件
export default [
    batchDispatchMiddleware,
    warn,
    log
]
  1. 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


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容