flux,redux,react-redux,redux-thunk,redux-saga,dva的区别

flux:

用过react的人都知道,当业务和交互逻辑变得复杂时,仅仅使用react会使得数据维护越来越复杂,组件之间共享数据时需要不断通过HOC(高阶组件)和render props的方式来解决,显然这种方式是不合理的,使得代码中出现了越来越多不必要的组件和逻辑;为了解决这个问题facebook推出了flux,它是一种架构思想,它是一种解决数据流通的方案;

具体思想就是单一数据源,把所有的需要跨组件共享的数据以及修改数据的方法都放在store中集中管理,需要使用共享数据的组件只需要获取store中的数据源,触发更改数据的action,dispatcher接收到action更新store中的数据,并触发store的onchange事件,组件监听到数据发生变化后重新获取新的数据进行渲染;

核心概念view,action,dispatcher,store

具体可以参考阮一峰老师的文章http://www.ruanyifeng.com/blog/2016/01/flux.html
补充
flux中可以有多个store

redux:

1:redux是将flux和函数式编程Elm结合在一起的一个状态管理库。
2:核心概念
state,action,reducer
state用来存放单一的数据源;
reducer用来更新state,并且是纯函数形式的更新,这样每个时刻的state都能够保存下来,时光机和撤销得以实现;

import { createStore, combineReducers} from 'redux';
//combineReducers参数对象中的key值对应着state中每个key的值,比如下面的state的返回值为{defaultState: {...}, firstState: {...}, secondState: {...}}
const rootReducer = combineReducers({
  defaultState,
  firstState,
  secondState,
});
//initState是初始化state的值;
const store = createStore(rootReducer, initState);

3:redux的api

  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器;
  • combineReducers:用来拆分reducer;

4:三大原则

  • 单一数据源
  • state是只读的,不能直接修改state
  • 只能通过纯函数执行修改

5:补充

  • store初始化之后,redux默认会dispatch一个action到reducer上,所以你的reducer需要提供一个default的case来返回初始的state;
  • redux中只有一个store

react-redux和redux-thunk

需要单独安装react-redux

npm install --save react-redux

1:通过容器组件和展示组件来实现,展示组件通过props获取和展示数据,通过connect将展示组件变成容器组件,容器组件通过mapStateToProps和mapDispatchToProps获取和更改state数据;
为了使得所有的容器组件能够访问到state数据
类组件写法

import { connect, Provider } from "react-redux";
import { createStore } from 'redux';
import reducers from './reducer.js';

const store = createStore(reducers);
<Provider store="store">
  <App /><!-- 子组件放在App下 -->
</Provider>

类组件写法

const mapStateToProps = (state) => {
  return {};
}

const mapDispatchToProps = (dispatch) => {
  return {};
}

@connect(mapStateToProps, mapDispatchToProps)
export default class Tes1 extends React.PureComponent{
  render() {}
}

@connect()
export default class Test2 extends React.PureComponent{
  render() {
    console.log(this.props.dispatch); //此处的dispatch就是全局的dispatch
  }
}

函数式组件写法

import { useDispatch, useSelector } from 'react-redux';
//hooks组件
const Test3 = (props) => {
  const state = useSelector(state => state);
  const dispatch = useDispatch();
  return (
    <div>123</div>
  );
}
export default Test1;

//普通函数组件
const Test4 = ({dispatch}) => {
  return (
    <div>123</div>
  );
}
export default connect()(Test2);

2:异步数据流处理
react-redux是不能处理异步数据的,目前可以通过中间件的方式解决,
以redux-thunk举例

redux-thunk介绍:
redux-thunk可以让你dispatch一个函数,该函数参数为(dispatch, getState),通过thunk.withExtraArgument可以注入额外的参数,比如:

const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument(api))
)

// actions
function fetchUser(id) {
  return (dispatch, getState, api) => {
    // you can use api here
  }
}

3:总体用法介绍

import React from 'react';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { createStore, combineReducers, applyMiddleware } from 'redux';

//reducers
function loadReducer (state, action) {
  switch(action) {
    case 'updateLoadStatus': 
      return {
        ...state,
        loadStatus: action.loadStatus
      }
      break;
    default:
      break;
  }
}

function dataReducer (state, action) {
  switch(action) {
    case 'updatePageData': 
      return {
        ...state,
        pageData: action.pageData
      }
      break;
    default:
      break;
  }
}

const rootReducer = combineReducers({loadReducer, dataReducer});

//actions
const actions = {
  fetchData: (params) => (dispatch, getState) => {
    return fetch(url, params).then(res => {
      dispatch({
        type: 'updatePageData',
        pageData: res.data
      });
    })
  }
};

const loggerMiddleware = createLogger();
const store = createStore(rootReducers, applyMiddleware(thunkMiddleware, loggerMiddleware));
const reducers = combineReducer();
//同步调用
store.dispatch({
  type: 'updateLoadStatus',
  loadStatus: 'loading'
})

//异步调用
store.dispatch(actions.fetchData({pageIndex: 0}))
.then(() => {console.log(store.getState())})

4:react-redux的api

  • Provider:将store通过context传递给子组件,注册对store的监听
  • connect:一旦store发生变化就会执行mapStatetoProps和mapDispatchtoProps获取最新的props,并传递给子组件触发子组件的更新

redux-saga

1:redux-saga 是一个用于管理 Redux 应用异步操作的中间件(又称异步 action)。 redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替 redux-thunk 中间件。
这意味着应用的逻辑会存在两个地方:

  • Reducers 负责处理 action 的 state 更新。
  • Sagas 负责协调那些复杂或异步的操作。

Sagas 是通过 Generator 函数来创建的,因为使用了 Generator,redux-saga 让你可以用同步的方式写异步代码。
Sagas 不同于 Thunks,Thunks 是在 action 被创建时调用,而 Sagas 只会在应用启动时调用(但初始启动的 Sagas 可能会动态调用其他 Sagas)。 Sagas 可以被看作是在后台运行的进程。Sagas 监听发起的 action,然后决定基于这个 action 来做什么:是发起一个异步调用(比如一个 Ajax 请求),还是发起其他的 action 到 Store,甚至是调用其他的 Sagas。
2:使用介绍
参考文章 https://chenyitian.gitbooks.io/redux-saga/content/
3:总结
使用 Effect 诸如 call 和 put,与高阶 API 如 takeEvery 相结合,让我们实现与 redux-thunk 同样的东西, 但又有额外的易于测试的好处。

dva

1:目前使用react最流行的数据流解决方案需要引入多个库,比较麻烦,dva将 react-router + react-redux + redux-saga3个工具库包装在一起,简化了api,使得开发更加快捷方便。
dva通过model将处理异步流程的effect和同步更新state的reducer, 以及订阅数据源的subscriptions封装在一起
2:使用介绍

import dva from 'dva';
const app = dva({
  initialState: {},//初始state,优先级高于model中的state
  history,//指定给路由用的history,默认是hashHistory
  onError,//effect 执行错误或 subscription 通过 done 主动抛错时触发,可用于管理全局出错状态。
  onAction,//action被dispath时触发,用于注册redux中间件
  onStateChange,//state改变时触发
  onReducer,//封装全局的reducer
  onEffect,//封装全局的effect
  onHmr,//
  extraReducers,//额外的reducer
  extraEnhancers,//额外的effect
});
app.model(model);//注册model
const model = {
  namespace: 'xxx',
  state: {},
  effects: {
    // 自动执行next的generator函数
    *effect({payload}, {call, put, select}) {
      // 用于获取所有model的state
      const todos = yield select(state => state.todos);
      yield call(异步函数, 异步函数参数);
      yield put({
        type: '',//同一个model,直接写reducer的名字,跨model需要加上命名空间'命名空间/reducer名',
        payload
      })
    }
  },
  reducers: {
    reducer(state, aciton) {
      return {};
    }
  },
  subscriptions: {
    setup({ history, dispatch }, done) {
      // 监听 history 变化,当进入 `/` 时触发 `load` action
      return unlistenFunction;//取消数据订阅,app.unmodel()必须取消数据订阅
    },
  }
}
app.use();//配置 hooks(opts 里也可以配所有的 hooks) 或者注册插件。(插件最终返回的是 hooks )
app.unmodel(namespace);//注销model
app.router(() => {})//单页注册路由表,多页返回jsx元素
// 单页
import { Router, Route } from 'dva/router';
app.router(({ history }) => {
  return (
    <Router history={history}>
      <Route path="/" component={App} />
    </Router>
  );
});
// 多页
app.router(() => <App />)
app.start('#root');//启动应用
最后通过connect将数据和组件串联起来,connect的组件通过props可以直接获取到dispatch,通过mapStateToProps获取所有model的数据。

补充:
browserHistory和hashHistory的区别:
browserHistory:路由跳转的时候通过改变path来实现,比如"https://www.baidu.com/a" -> “https://www.baidu.com/b”,浏览器会想服务器请求新的页面资源,需要服务器做特殊的处理使得path改变的时候不新增页面

hashHistory:路由跳转的时候通过改变hash来实现,浏览器不会发起新的请求,只是改变了hash值,通过window.addEventListener(‘hashchange’,function(e) {/** e.oldURL,e.newURL / },false);监听hash值的变化
3:总结
解决了react没有解决的几个问题:
1:组件之间如何
通信**
2:数据如何和视图串联起来,异步数据如何处理,路由和数据如何绑定?

写在最后:

本文是本人作为个人笔记所用,后面会不断更新完善;如有想一起学习进步的小伙伴,欢迎交流

你可能感兴趣的:(react,react,状态管理方案)