Redux工作流程及中间件实现

205 阅读3分钟

概念

  1. JS的状态容器,提供可预测化的状态管理

  2. 工作流程

  • Viwe --dispatch--> Actions --middleware--> Reducer --> Store --subscribe--> View
  • Actioss:对象,type属性描述对状态进行怎样的操作
  • Reducer:函数,操作状态并返回新的状态
  • Store:存储状态的容器,JS对象
  • View:视图,HTML页面
  1. 核心API
// 1、创建store的状态容器
const store = Redux.createStore(reducer);
// 2、处理状态的reducer函数
function reducer(state = initialState,action){}
// 3、获取状态
store.getState();
// 4、订阅状态
store.subscribe(function(){});
// 5、触发Action
store.dispatch({type: 'description...'});

React + Redux

  1. 作用

在React中组件通信的数据流是单向的, 顶层组件可以通过props属性向下层组件传递数据, 而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组件传递修改数据的方法到下层组件.当项目越来越大的时候,组件之间传递数据变得越来越困难.使用Redux管理数据, 由于Store独立于组件,使得数据管理独立于组件, 解决了组件与组件之间传递数据困难的问题。

  1. react-redux模块
  • Provider:将我们创建出来的store放在一个全局的,组件够得着的地方
ReactDOM.render(
    <Provider store={store}><Counter></Provider>,
    document.getElementById('root')
)
  • connect:会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件;可以让我们获取store中的状态、dispatch 方法,将状态和方法通过组件的props属性映射给组件
function Counter({count, add}){
    return <div>
        <button onClick={add}>++</button>
        <span>{count}</span>
    </div>
}

const mapStateToProps = state => ({
    count: state.count
})
const mapDispatchToProps = dispatch => ({
    add(){
        dispatch({type: 'add'})
    }
})

export default connect(mapStateToProps, mapDispatchToProps)(Counter)
  1. bindActionCreators简化代码
import { bindActionCreators } from 'redux'
import * as counterActions from './counter'
...
mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch)
export const add = (payload) => ({type: 'add',payload})
...

中间件

  1. 允许我们扩展redux应用程序的函数

  2. 使用案例

  • logger
export default store => next => action => {
    console.log(store)
    console.log(action)
    next(action)
}
  • store
import { createStore, applyMiddleware } from 'redux'
import { RootReducer } from './reducers/root.reducer'
import { logger } from './middlewares/logger'
// 其他中间件
import { test } from './middlewares/test'
// applyMiddleware用来注册中间件
createStore(RootReducer, applyMiddleware(logger, test))
  1. thunk
export default ({dispatch}) => next => action => {
    if(typeof action === 'function'){
        return action(dispatch)
    }
    next(action)
}
// 使用案例
export const add_async = () => dispatch => {
    setTimeout(()=>{ dispatch(add())}, 2000)
}

源码实现

  1. createStore
function createStore (reducer, preloadedState) {
    var currentState = preloadedState;
    var currentListeners = [];
    function getState(){
        return currentState;
    }
    function dispatch(action){
        currentState = reducer(currentState, action)
        for (var i = 0; i < currentListeners.length; i++){
            var listener = currentListeners[i];
            listener();
        }
    }
    function subscribe(listener) {
        currentListeners.push(listener);
    }
    return {
        getState,
        dispatch,
        subscribe,
    }
}
  1. applyMiddleware
function applyMiddleware(...middlewares){
    return function(createStore){
        return function(reducer, preloadedState){
            var store = createStore(reducer, preloadedState);
            var middlewareAPI = {
                getState: store.getState,
                dispatch: store.dispatch,
            }
            var chain = middlewares.map(middleware => middleware(middlewareAPI))
            var dispatch = compose(...chain)(store.dispatch)
            return {
                ...store,
                dispatch,
            }
        }
    }
}
function compose(){
    var func = [...arguments];
    return function(dispatch){
        for (var i = func.length-1; i>=0; i--){
            dispatch = func[i](dispatch)
        }
        return dispatch
    }
}
  1. bindActionCreators
function bindActonCreators (actionCreators, dispatch){
    var boundActionCreator = {};
    for(var key in actionCreators){
        (function(key){
            boundActionCreators[key] = function(){
                dispatch(actionCreators[key]())
            }
        })(key)
    }
    return boundActionCreators;
}
  1. combinReducers
function combinReducers(reducers){
    var reducerKeys = Object.keys(reducers);
    for(var i = 0; i < reducerKeys.length; i++){
        var key = reducerKeys[i];
        if(typeof reducers[key] !== 'function') throw new Error('')
    }
    return function (state, action) {
        var nextState = {};
        for(var i = 0; i < reducers.length; i++){
            var key = reducers[i];
            var reducer = reducers[key];
            var preState = state[key];
            nextState[key] = reducer(preState, action);
        }
        return nextState;
    }
}

Immutable

实现了结构共享,当我们更新一个节点,只有几个节点需要被重新创建。在添加、修改、删除操作后,我们避免了将 map 中所有值拷贝一遍,所以特别是在数据量较大时,这些操作相比Object.assign有明显提升。