react系列之flux、redux、mobx、dva

背景

  • react 功能单一,用于 UI 渲染
  • redux 用来管理数据
  • react-router 用来管理路由
  • webpack 用来配置工程
  • ES6 让代码更加优雅简洁

flux

基本概念
View:视图层,接收用户输入、监听状态改变
Action:视图层发出的消息
Dispatcher:接收Actions,执行回调函数
Store:存放应用的状态,一旦状态改变,通知View更新页面

特点:数据单向流动

Action -> Dispatcher -> Store -> View -> Action

行为:

  1. 用户操作 View,View 层接收用户的输入
  2. 调用 Dispatcher 提供的 dispatch 方法,发送一个 Action 对象,包括 type 类型 和 其他参数
  3. Dispatcher 的register 方法了里面登记了各种 Action 的回调函数,当 dispatch 方法被调用的时候,就会执行这个回调函数,接收 Action ,根据 type 通知 Store 进行相应的更新
  4. Store 更新完毕,发送一个 change 事件
  5. View 视图组件在 ComponentDidMount 的时候监听 change 事件,当 change 触发之后,更新组件自身的 state
  6. 组件在 ComponentWillUnmount 的时候移除监听事件。

和看阮大大的demo一起理解上述文字更清晰:http://www.ruanyifeng.com/blog/2016/01/flux.html

redux

redux的思想:就是把所有的状态都放在一个统一个store中,当你想要改变状态的时候就要触发一个action,action编写reducer去改变state,整个state的改变是在reducer中发生的,不会有任何的副作用。

特征

  • 所有的 state 都是以一个对象树的形式存储在一个单一的 store 中。
  • 唯一改变 state 的方法就是触发 action,
  • action 如何改变 state 需要编写 reducers。

与 Flux 的区别

  • Redux 没有 Dispatcher
  • 不支持多个 store

优化

把根级的 reducer 拆成多个小的 reducers

三大原则

单一数据源

整个应用的 state 存储在一棵对象树中,并且这个对象树存在于唯一的 store 中。

State 是只读的

唯一改变 state 的方法就是触发 action,强制使用 action 来描述所有变化可以清晰的知道应用中到底发生了什么。

使用纯函数来执行修改

为了描述 action 是如何更改 state 的,需要编写 reducer,随着应用变大,你可以把它拆成多个小的 reducers

纯函数:接收一个 state 和 action,并返回新的 state 的函数

import { combineReducers, createStore } from "redux";
let reducer = combineReducers({ visibilityFilter, todos });
let store = createStore(reducer);

store

store 保存整个应用的state,并且还提供一些方法,比如:

  • 通过 getState() 访问state
  • 通过 dispatch(action) 修改state
  • 通过 subscribe(listener) 注册listener

action

action 是一个纯 javascript 对象,包括 type,payload 属性,type 表示 action 的类型,指出应该触发reducer中的哪个函数来修改state

reducer

reducer 是一个纯函数,以 先前的 state 和 一个 action 为参数,返回一个新的 state 的函数。

Redux Thunk 的作用是什么

Redux Thunk 是一个允许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。

如果满足某个条件,Thunk 可以用来延迟 action 的派发、异步处理 action的派发。

dva

优点

  • 框架:dva 是一个前端应用框架,集成了 redux,redux-saga,redux-router-redux,react-router
  • 快速初始化: 可以快速实现项目的初始化,不需要繁琐地配置
  • 简洁的 api: 整个项目中只有 dva、app.model、app.router、app.use、app.start 几个 API
  • 简洁开发:将 initState、saga、reducer 集成到一个 model 里面统一管理,避免文件散落在各个文件里面,便于快速查找与开发

定义 dva 中的 model:

export default {
  namespace: "user", // 命名模块名称
  state: {
    // model中的状态
    profile: null
  },
  subscriptions: {
    //组件的所有生命周期都可以在这里找到对应的api去调用
    setup({ dispatch }) {
      dispatch({
        type: "initUserInfo"
      });
    }
  },
  effects: {
    // 处理所有的网络请求
    *initUserInfo({ payload }, { call, put, select }) {
      let userState = yield select(state => state.user.profile);
      let user = yield call(userService.fetUserInfo);
      if (!userState) {
        yield put({
          type: "setUserProfileReducer",
          profile: user
        });
      }
    }
  },
  reducers: {
    // 改变state
    setUserProfileReducer(state, { profile }) {
      return {
        ...state,
        profile
      };
    }
  }
};
  • namespace: model 的命名
  • state:model 里面的数据
  • subscriptions:常用的是在组件渲染之前触发
    • setup
  • effects:所有网络请求都应该放在这里面,应为它使用了 generator 来处理异步,这里是唯一一个地方可以处理异步请求的。
    • *网络请求(入参,{call, put, select}){}
    • const data = yield call( apiFunction ); // 发起网络请求,获取请求回来的结果
    • const data = yield put( { type : “save” , payload :{data} ); // 触发 reducer,改变 state
    • const data = yield select( { modelName } => modelName.stateValue ); // 所有的 model 的 state 都是共享的,你可以在其他的 model 中拿到这个 model 的 state。
    • import { routerRedux } from ‘dva/router’; yield put( routerRedux.push("/targit")); // 组件之间的跳转使用
  • reducers:改变 state 的函数
    • 方法(state,{newstate}) // 当前 model 的 state 和数据,以及传入的数据,对 state 进行修改。

dispatch({ type: ‘fetch’}); //如果是当前 model 可以直接写方法名如果是别的 model 的那你需要前面加上 model 名字 { type: ‘OtherModel/fetch’}

在组件中使用 model:

  • 通过函数参数方式传入
import { connect } from "dva";
// dispatch 用来发起一个请求
const IndexPage = ({ dispatch, state }) => {
  // 在组件的事件中触发
  function(){
    dispatch({
      type : "modelName/name"
      payload : ""
    })
  }
  return <Index />;
};
// 使用dva的connect方法连接组件和model
export default connect(({ state }) => ({
  state
}))(IndexPage);
  • 也可以用 class 来声明一个组件
import { connect } from "dva";
class AppointmentPage extends React.Component {
  handleClick(){
    // dispatch方法会在this.props里面。
    this.props.dispatch({
      type: 'modelName/name'
    })
  }
  render(){
    return (...)
  }
}
export default connect(({ modelName }) => ({
 modelName
}))(Component);

学习 dva 的一些体悟 https://juejin.im/post/59c5b29b5188257e8c55000b

mobx

编写 store:

import { observable, action } from "mobx";
class MyStore {
  @observable list = [];
  @computed
  get total() {
    return this.price * this.amount;
  }
  @action
  async fetch({ payload, resolve, reject }) {
    try {
      let res = await services.fetch(payload);
      if (res) {
        this.list = res.data.data;
        resolve && resolve();
      }
    } catch (e) {
      reject && reject();
      console.error(e);
    }
  }
}
const myStore = new MyStore();
export default myStore;
  • @observable 将状态转换成可观察的
  • @computed 计算值,根据现有的状态或者其他计算值衍生出来的值
  • @action 动作,用来修改状态的
    • {payload,resolve,reject} payload:入参 resolve 成功的回调 reject 失败的回调 // 这些入参都是自定义的
    • const data = await services.fectch(payload); // await 执行异步的请求 payload 为请求的入参
    • this.state = 新的值

在组件中使用 mobx:

import { inject, observer } from 'mobx-react';
@inject('MyStore')
@observer
class Example extends React.Component{
  componentDidMount() {
    this.props.MyStore.fetch()
  }
}
export defaultExample;
  • @inject 注入这个 store
  • @observer 将 react 组件转变成响应式组件

store 将存在于组件的 props 中。更多 api 请移步: http://cn.mobx.js.org/

你可能感兴趣的:(react,dva,mobx+react,@inject,flux数据流)