Redux不止是使用

Redux的设计思想很简单,一、web应用是一个状态机,视图与状态一一对应;二、全部状态保存在一个对象里
复制代码

基本概念和API

  1. Store 应用状态的管理者,能够看做一个数据库,包含如下函数web

    const store = {
            dispatch(action),               // 触发state改变的惟一方法
            subscribe(listener),            // 订阅函数,store变化触发的订阅的监听函数,返回取消订阅的方法
            getState(),                     // 获取state的方法
            replaceReducer(nextReducer),    // 替换reducer
            [$$observable]: observable
          }
    复制代码

    一个应用只应有一个单一的 store,其管理着惟一的应用状态 state Redux提供createStore函数,用于生成store数据库

    import { createStore } from 'redux';
    const store = createStore(reducer);
    复制代码
  2. statejson

    一个对象,包含全部数据,能够看做数据库中的数据,经过const state = store.getState()获取redux

  3. Actionapi

    一个包含type属性的对象,做用是描述如何修改state,它会运输数据到store,做为reducer函数的参数数组

  4. Action Creatorbash

    生成Action的函数app

  5. dispatch异步

    view发出Action的惟一方法,redux规定不能直接在业务代码中修改state,若想要修改,只能经过store.dispatch(Action)实现async

  6. Reducer

    一个纯函数,会根据Acion的type值生成新的state替换调原来调state

源码解析

只对关键代码进行解读。

  1. 目录结构

  1. index.ts

    先看入口文件

    import createStore from './createStore'
    import combineReducers from './combineReducers'
    import bindActionCreators from './bindActionCreators'
    import applyMiddleware from './applyMiddleware'
    import compose from './compose'
    import warning from './utils/warning'
    import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
    
    //...
    
    export {
      createStore,
      combineReducers,
      bindActionCreators,
      applyMiddleware,
      compose,
      __DO_NOT_USE__ActionTypes
    }
    复制代码

    很简单,导出一个对象,接下来一个个分析

  2. createStore.ts

    import $$observable from './utils/symbol-observable'
        
        import {
          Store,
          PreloadedState,
          StoreEnhancer,
          Dispatch,
          Observer,
          ExtendState
        } from './types/store'
        import { Action } from './types/actions'
        import { Reducer } from './types/reducers'
        import ActionTypes from './utils/actionTypes'
        import isPlainObject from './utils/isPlainObject'
        
    
        export default function createStore (
          reducer,   // 由于dispatch会自动触发reducer的执行,因此在生成store的时候须要把reducer函数传递进来
          preloadedState?,  // 初始state
          enhancer?  // 加强,经过一系列中间件加强dispatch函数,例如对于一步Action对处理,后面详细介绍
        ) {
            // ...对参数的判断
            
            if (typeof enhancer !== 'undefined') {
                if (typeof enhancer !== 'function') {
                  throw new Error('Expected the enhancer to be a function.')
                }
                return enhancer(createStore)(
                  reducer,
                  preloadedState as PreloadedState<S>
                ) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
              }
        
            let currentReducer = reducer                      // reducer函数
            let currentState = preloadedState                 // 当前state
            let currentListeners = []  // 用于存储订阅的回调函数,dispatch 后逐个执行
            let nextListeners = currentListeners              // 下一个事件循环的回调函数数组
            let isDispatching = false                         // 是否正在dispatch
            
            // 判断当前的回调函数队列是否与下一个回调函数队列相同,不能影响本次的执行
            function ensureCanMutateNextListeners() {
                if (nextListeners === currentListeners) {
                  nextListeners = currentListeners.slice()
                }
            }
        
    
            // 获取state
            function getState() {
              // 直接返回state
              return currentState
            }
          
            // 订阅--取消订阅
            function subscribe(listener) {
        
                let isSubscribed = true
        
                ensureCanMutateNextListeners()
                nextListeners.push(listener)
        
                // 取消订阅
                return function unsubscribe() {
                    if (!isSubscribed) {
                        return
                    }
        
                    isSubscribed = false
        
                    ensureCanMutateNextListeners()
                    // 将取消订阅的回调函数从数组中移除
                    const index = nextListeners.indexOf(listener)
                    nextListeners.splice(index, 1)
                    currentListeners = null
                }
            }
        
            // 派发一个Action用于改变state
            // 若是dispatch的不是一个对象类型的action(同步),而是一个Promise/Thunk(异步),
            // 就须要引入redux-thunk 等中间件来反转控制权,具体会在applyMiddlewares()方法中解析
            function dispatch(action) {
                // 确保是一个普通的对象,若非普通对象,原型链上会出现其余属性
                if (!isPlainObject(action)) {
                    throw new Error(
                        'Actions must be plain objects. ' +
                        'Use custom middleware for async actions.'
                    )
                }
        
                // 确保拥有type属性
                if (typeof action.type === 'undefined') {
                    throw new Error(
                        'Actions may not have an undefined "type" property. ' +
                        'Have you misspelled a constant?'
                    )
                }
        
                // 是否正在dispatch
                if (isDispatching) {
                    throw new Error('Reducers may not dispatch actions.')
                }
        
                try {
                    isDispatching = true
                    // dispatch 触发reducer函数,返回state,赋值给currentState,这也是为何咱们要在建立store对象的时候传入reducer
                    currentState = currentReducer(currentState, action)
                } finally {
                    isDispatching = false
                }
        
                // 逐个执行订阅的回调函数
                const listeners = (currentListeners = nextListeners)
                for (let i = 0; i < listeners.length; i++) {
                    const listener = listeners[i]
                    listener()
                }
                return action
            }
        
            // 替换当前的reducer
            function replaceReducer (
                nextReducer
            ) {
        
                // 直接替换,简单粗暴
                currentReducer = nextReducer
            
                dispatch({ type: ActionTypes.REPLACE })
                return store
            }
        
            // 这是留给 可观察/响应式库 的接口,具体实现能够自行搜索一下
            function observable() {
                const outerSubscribe = subscribe
                return {
              
                    subscribe(observer) {
                        if (typeof observer !== 'object' || observer === null) {
                            throw new TypeError('Expected the observer to be an object.')
                        }
        
                        function observeState() {
                        const observerAsObserver = observer
                        if (observerAsObserver.next) {
                            observerAsObserver.next(getState())
                        }
                    }
        
                    observeState()
                    const unsubscribe = outerSubscribe(observeState)
                    return { unsubscribe }
                },
        
                [$$observable]() {
                    return this
                }
            }
          }
        
          // 初始化state
          dispatch({ type: ActionTypes.INIT })
        
          // 返回store对象
          const store = {
            dispatch,
            subscribe,
            getState,
            replaceReducer,
            [$$observable]: observable
          }
          return store
        }
    
    复制代码

    createStore(reducer, preloadState?, enhancer?)是Redux的核心代码,它给咱们提供了获取state的方法getState()、触发state改变的方法dispatch(Action)等

  3. combineReducers.ts

import { Reducer } from './types/reducers'
    import { AnyAction, Action } from './types/actions'
    import ActionTypes from './utils/actionTypes'
    import warning from './utils/warning'
    import isPlainObject from './utils/isPlainObject'
    import {
      ReducersMapObject,
      StateFromReducersMapObject,
      ActionFromReducersMapObject
    } from './types/reducers'
    import { CombinedState } from './types/store'
    
    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers: ReducersMapObject = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
      }
      // 全部reducer的key集合
      const finalReducerKeys = Object.keys(finalReducers)

      let unexpectedKeyCache
      
    // 返回合成后的 reducer
    return function combination(
        state,
        action
    ) {
        let hasChanged = false
        const nextState = {}
        for (let i = 0; i < finalReducerKeys.length; i++) {
          const key = finalReducerKeys[i]
          const reducer = finalReducers[key]
          const previousStateForKey = state[key]                        // 获取当前子的state
          const nextStateForKey = reducer(previousStateForKey, action)  // 执行起对应的reducer
          nextState[key] = nextStateForKey                              // 将子nextState挂载到对应的键名
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        return hasChanged ? nextState : state                           // 返回总的state
      }
    }

复制代码

假如咱们的应用有两个状态须要管理,一个是打印日志,另外一个是计数器功能,咱们能够把它们写在一个reducer中,但若是还有其余动做,这个堆在一个会让代码变得难以维护,因此最好但办法是拆分reducer,使每个动做变得单一,最后在整合在一块儿,combineReducers()就是为此而生的。须要注意的是,子reducer的名称应该与起对应的state相同。

好比咱们的目录结构以下

reducers/
   ├── index.js
   ├── counterReducer.js
   ├── todosReducer.js
复制代码

就能够这样实现

/* reducers/index.js */
import { combineReducers } from 'redux'
import counterReducer from './counterReducer'
import todosReducer from './todosReducer'

const rootReducer = combineReducers({
  counter: counterReducer, // 键名就是该 reducer 对应管理的 state
  todos: todosReducer
})

export default rootReducer

-------------------------------------------------

/* reducers/counterReducer.js */
export default function counterReducer(counter = 0, action) { // 传入的 state 实际上是 state.counter
  switch (action.type) {
    case 'INCREMENT':
      return counter + 1 // counter 是值传递,所以能够直接返回一个值
    default:
      return counter
  }
}

-------------------------------------------------

/* reducers/todosReducers */
export default function todosReducer(todos = [], action) { // 传入的 state 实际上是 state.todos
  switch (action.type) {
    case 'ADD_TODO':
      return [ ...todos, action.payload ]
    default:
      return todos
  }
}
复制代码

这样作的好处不言而喻。不管您的应用状态树有多么的复杂,均可以经过逐层下分管理对应部分的 state:

counterReducer(counter, action) -------------------- counter
                              ↗                                                              ↘
rootReducer(state, action) —→∑     ↗ optTimeReducer(optTime, action) ------ optTime ↘         nextState
                              ↘—→∑                                                    todo  ↗
                                   ↘ todoListReducer(todoList,action) ----- todoList ↗


注:左侧表示 dispatch 分发流,∑ 表示 combineReducers;右侧表示各实体 reducer 的返回值,最后汇总整合成 nextState
复制代码
  1. bindActionCreator.ts
import { Dispatch } from './types/store'
    import {
      AnyAction,
      ActionCreator,
      ActionCreatorsMapObject
    } from './types/actions'
    
    function bindActionCreator<A extends AnyAction = AnyAction>(
      actionCreator: ActionCreator<A>,
      dispatch: Dispatch
    ) {
      return function (this: any, ...args: any[]) {
       // 给Action Creator 加上dispatch技能,也就是包裹一下
        return dispatch(actionCreator.apply(this, args))
      }
    }

    
    export default function bindActionCreators(
      actionCreators,
      dispatch
    ) {
        // 处理actionCreators是函数的状况
        if (typeof actionCreators === 'function') {
            return bindActionCreator(actionCreators, dispatch)
        }
    
        if (typeof actionCreators !== 'object' || actionCreators === null) {
            throw new Error(
              `bindActionCreators expected an object or a function, instead received ${
                actionCreators === null ? 'null' : typeof actionCreators
              }. ` +
                `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
            )
        }
        
        // 处理actionCreators是对象的状况
        const boundActionCreators = {}
        for (const key in actionCreators) {
            const actionCreator = actionCreators[key]
            if (typeof actionCreator === 'function') {
              boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
            }
        }
        return boundActionCreators
    }
复制代码

bindActionCreators(actionCreators, dispatch)这个函数的功能也比较单一,就是dispatch(ActionCreator(XXX)),这个方法在异步状况下,没什么卵用。。。

  1. compose.ts
export default function compose(...funcs) => 

// 复合函数 compose(func1, func2, func3)(0) => func3(func2(func1(0)))
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}

复制代码

这个函数很是简单,功能也很单一,主要用了数组的reduce方法,将函数做为reduce的回调函数的参数,参考文档

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
复制代码
  1. applyMiddlewares.ts

中间件就是一个函数,对dispatch进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其余功能。

它是 Redux 的原生方法,做用是将全部中间件组成一个数组,依次执行。目的是为了在dispatch先后,统一处理想作对事。

import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import { StoreEnhancer, StoreCreator, Dispatch } from './types/store'
import { Reducer } from './types/reducers'

export default function applyMiddleware(
  ...middlewares
) {
  return (createStore) => (
    reducer,
    ...args
  ) => {
    const store = createStore(reducer, ...args)
    let dispatch = () => {
    }
    // 中间件增长api
    const middlewareAPI = {
      getState,
      dispatch
    }
    // chain存放中间件的数组,逐个增长api
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // compose复合函数 a(b(c(0))) => compose(a, b, c)(0)
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
复制代码

从源码能够看出,这个函数的主要做用就是经过各类中间件对dispatch进行加强,借此咱们就能够处理异步Action等问题了。

咱们能够思考一下为何处理异步Action就须要对dispatch进行加强,缘由以下:

(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其余功能,也承担不了,由于理论上,纯函数不能进行读写操做。

(2)View:与 State 一一对应,能够看做 State 的视觉层,也不合适承担其余功能。

(3)Action:存放数据的对象,即消息的载体,只能被别人操做,本身不能进行任何操做。

因此只有发送 Action 的这个步骤,即store.dispatch()方法,能够添加功能。

异步操做的基本思路是什么呢?

同步操做只须要发出一种Action,异步操做则须要发出3种,发送时、成功时、失败时,例如:

{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
复制代码

一样的state也须要进行对应的改造

let state = {
    // ... 
    isFetching: true,       // 是否正在抓去数据
    didInvalidate: true,    // 数据是否过期
    lastUpdated: 'xxxxxxx'  // 更新数据时间
}
复制代码

因此异步操做的思路应该是:

  • 操做开始时,送出一个 Action,触发 State 更新为"正在操做"状态,View 从新渲染
  • 操做结束后,再送出一个 Action,触发 State 更新为"操做结束"状态,View 再一次从新渲染

Redux-Thunk

前面说处处理异步Action须要借助到redux-thunk等中间件,如今具体分析一下redux-thunk是怎么处理异步请求的。

异步操做至少要送出两个 Action:用户触发第一个 Action,这个跟同步操做同样,没有问题;如何才能在操做结束时,系统自动送出第二个 Action 呢?

最主要的就是在Action Creator中处理,例如store.dispatch(fetchPosts(selectedPost))

fetchPosts()就是一个Action Creator,看一下内部实现:

const fetchPosts = postTitle => (dispatch, getState) => {
  // 先发送第一个Action,同步的 
  dispatch(requestPosts(postTitle));
  return fetch(`/some/API/${postTitle}.json`)
    .then(response => response.json())
    .then(json => 
        // 请求到结果后,发送第二个Action,异步
        dispatch(receivePosts(postTitle, json)
    ));
  };
};
复制代码

值得注意的是,Action Creator返回的是一个函数,而正常返回应该是一个对象,而且这个函数的参数是dispatch和getState两个方法。

这个咱们就须要用到redux-thunk帮咱们处理一下,看下源码

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => (next) => (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
复制代码

竟是如此简单的函数,这里咱们结合applyMiddleware分析一下:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';

const store = createStore(
  reducer,
  {},
  applyMiddleware(thunk)
);
复制代码

applyMiddleware函数的参数是thunk函数 ==>

({ dispatch, getState }) => (next) => (action) => {
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }
        return next(action);
    }
复制代码

回顾一下applyMiddleware函数 ==>

import compose from './compose'
import { Middleware, MiddlewareAPI } from './types/middleware'
import { AnyAction } from './types/actions'
import { StoreEnhancer, StoreCreator, Dispatch } from './types/store'
import { Reducer } from './types/reducers'

export default function applyMiddleware(
  ...middlewares
) {
  return (createStore) => (
    reducer,
    ...args
  ) => {
    const store = createStore(reducer, ...args)
    let dispatch = () => {
    }
    // 中间件增长api
    const middlewareAPI = {
      getState,
      dispatch
    }
    // chain存放中间件的数组,逐个增长api
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // compose复合函数 a(b(c(0))) => compose(a, b, c)(0)
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
复制代码

应用了redux-thunk,dispatch函数就会变成

dispatch = (store.dispatch) => (action) => {
        // 处理action是函数的状况
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }
        
        // 处理action是对象的状况
        return store.dispatch(action)
    }
复制代码

总结了这些,若有不对的地方还望你们指正,但愿有兴趣的朋友一块儿交流交流。

相关文章
相关标签/搜索