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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章