深入浅出ーReduxソース


目次
  • 、ステータスツリーを維持し、
  • を発行します.
  • 1.1 dispatch
  • .subscribe
  • 1.3 get State
  • 1.4 replacceReducer
  • 1.5[$observable]:observable
  • 、ミドルウェア
  • 、ツール関数
  • 3.1 compse
  • 3.2 cobineReduces
  • 3.3ビッドアクションクリエーター
  • 付録
  • 本文の前提条件を読み、Reduxの基本概念を知る.
    雑談:
      フレームを理解するには、まず分かります.
    フレームの痛みは何ですか?
      フロントエンドは状態管理(同期状態、非同期状態)が必要です.正直に言って、簡単にコードを書くのは状態管理ですが、これは私達の追求ですか?もちろんそうではありません.私たちは先端の状態を優雅に管理する必要があります.MVCアーキテクチャでは、データ(Model)----インターフェース(View)----インタラクティブ操作(Controller)に簡単にマッピングすることができる.
      状態管理とは基本的にデータとインタラクティブ操作の管理です.Reduxは基本的にこのようなことをしました.
    まとめ:
    Reduxソースは基本的に二つの操作をしました.
    1)状態ツリーのメンテナンス
    2)ステータスのリリース購読
    一、ステータスツリーを維持し、購読を発表する
    /**      
    *  @param {Function} reducer:reducer       ,actionType---->new state
    *  @param {any} preloadedState: store     ,       ,     ,                      merge
    *  @param {Function} enhancer:    , action             
    *  @returns {Store} :        ,   Store       ,  、   
    */
    export default function createStore(reducer, preloadedState, enhancer) {
    	 //     , preloadedState enhancer           ,                   
    	 if (
    		    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    		    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
      	) {
    		    throw new Error(
    		      'It looks like you are passing several store enhancers to ' +
    		        'createStore(). This is not supported. Instead, compose them ' +
    		        'together to a single function'
    		    )
      	}
    	if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
        		enhancer = preloadedState
        		preloadedState = undefined
      	}
      	/**
      	*         ,              ,            ?         
      	*  @params {createStore}  
      	*  @params {reducer, preloadedState}
      	*/
     	 if (typeof enhancer !== 'undefined') {
       		 if (typeof enhancer !== 'function') {
          		throw new Error('Expected the enhancer to be a function.')
        	}
        	return enhancer(createStore)(reducer, preloadedState)
    	 }
    	 /**
    	 *        ,         ,  Store
    	 *  @variable 
    	 * 		-currentReducer:   reducer,       reducer
    	 * 		-currentState:     ,    state    ,      ,undefiend
    	 * 		-currentListener:       ,listener      
    	 * 		-nextListener:      
    	 * 		-isDispatching:            ,          ,       ,        ,   ,   ,       
    	 */
    	  let currentReducer = reducer
    	  let currentState = preloadedState
    	  let currentListeners = []
    	  let nextListeners = currentListeners
    	  let isDispatching = false
    	/**
    	*        store             
    	*/
    	  return {
    	    dispatch,
    	    subscribe,
    	    getState,
    	    replaceReducer,
    	    [$$observable]: observable
    	  }
    }
    
    1.1 dispatch
    /**
       * Dispatches an action.      store   
       *
       * @param {Object} action        ,plainObject
       *
       * @returns {Object}           ,       
       *
       *       ,            ,    `dispatch()`   ,       ,        plainObject
       * 
       *      ,      ,        ,             
       */
      function dispatch(action) {
      
        try {
          isDispatching = true
          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
      }
    
    1.2 subscribe
    /**
       *          ,     callback       
       * @param {Function}  dispatch        
       * @returns {Function}            
       */
      function subscribe(listener) {
    
        let isSubscribed = true
    
        ensureCanMutateNextListeners()
        nextListeners.push(listener)
    
        return function unsubscribe() {
          if (!isSubscribed) {
            return
          }
    
          if (isDispatching) {
            throw new Error(
              'You may not unsubscribe from a store listener while the reducer is executing. ' +
                'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
            )
          }
    
          isSubscribed = false
    
          ensureCanMutateNextListeners()
          const index = nextListeners.indexOf(listener)
          nextListeners.splice(index, 1)
        }
      }
    
    1.3 get State
    /** 
       * @returns {any}               
       */
      function getState() {
        if (isDispatching) {
          throw new Error(
            'You may not call store.getState() while the reducer is executing. ' +
              'The reducer has already received the state as an argument. ' +
              'Pass it down from the top reducer instead of reading it from the store.'
          )
        }
        return currentState
      }
    
    1.4 replacceReducer
    /**
       * @param {Function}     reducer,       
       * @returns {void}
       */
      function replaceReducer(nextReducer) {
        if (typeof nextReducer !== 'function') {
          throw new Error('Expected the nextReducer to be a function.')
        }
    
        currentReducer = nextReducer
        dispatch({ type: ActionTypes.REPLACE })
      }
    
    
    1.5[$observable]:observable
    /**   observabla  ,      ,        ,      Redux      ,        
       * [Observable  ](https://github.com/tc39/proposal-observable)
       * [Observable  ](https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/)
       * subscribe :           
       */
      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() {
              if (observer.next) {
                observer.next(getState())
              }
            }
    
            observeState()
            const unsubscribe = outerSubscribe(observeState)
            return { unsubscribe }
          },
    
          [$$observable]() {
            return this
          }
        }
      }
    
    中間部品
    /**
     * redux         ,  
     * @param {...Function} middlewares       
     * @returns {Function}     enchancer      
     *             , reducer state   ,           
     *     	return enhancer(createStore)(reducer, preloadedState)
     */
    export default function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = () => {
          throw new Error(
            `Dispatching while constructing your middleware is not allowed. ` +
              `Other middleware would not be applied to this dispatch.`
          )
        }
    
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
    
        return {
          ...store,
          dispatch
        }
      }
    }
    
    三、ツール関数
    上のredux基本原理のソースコードは、基本的に終了しました.上記以外にもいくつかのツール関数compose、cobineReducers、bindActioCreatorsの3つのツール関数を提供しています.
    3.1 compse
      composeは主に中間部品を組み合わせる時に中間部品を組み合わせます.結局は一つの関数しか受け入れられません.そして要求関数は順番に一つずつ実行します.
    /**
    * compose       ,           
    * compose (f, g, h)===>  (...args) => f(f(h(...args)))
    * reduce          ,        
    */
    function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
        return funcs[0]
      }
    
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    
    3.2 commbineReducers
    commbineReducersは主に複数のreducersを一つのreducersに組み合わせるために使われています.reduxを書いたのは一般的にContactainersを書いています.各containersはreducersがありますが、これらのreducersをどうやって組み合わせたらいいですか?commbineReducersがするのはこのことです.結局Redux要求は一つのStoreだけが一つのReducersです.
    /**
     * @param {Object}          ,       reducers
     *
     * @returns {Function}           reducers        
     *      finalReducers  ,       reducers  ,    reducers(      ,        )
     */
    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key]
        }
      }
      const finalReducerKeys = Object.keys(finalReducers)
    
      let unexpectedKeyCache
      if (process.env.NODE_ENV !== 'production') {
        unexpectedKeyCache = {}
      }
      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]
          const nextStateForKey = reducer(previousStateForKey, action)
          if (typeof nextStateForKey === 'undefined') {
            const errorMessage = getUndefinedStateErrorMessage(key, action)
            throw new Error(errorMessage)
          }
          nextState[key] = nextStateForKey
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        return hasChanged ? nextState : state
      }
    }
    
    3.3ビッドアクションクリエーター
    ビッドアクションクリエーターの役割は、dispatchを隠すために、dispatchを実行可能な関数に変えます.核心はビッドアクションクリエーターです.
    function bindActionCreator(actionCreator, dispatch) {
      return function() {
        return dispatch(actionCreator.apply(this, arguments))
      }
    }
    
    しかし、多くのactionsがありますので、各actionのためにdispatchを隠すために、便利な操作が必要です.
    export default function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    
      const keys = Object.keys(actionCreators)
      const boundActionCreators = {}
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const actionCreator = actionCreators[key]
        if (typeof actionCreator === 'function') {
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
      }
      return boundActionCreators
    }
    
    
    付録
    どのように対象がPlainObjectかを判断します.自分で理解してください.書けなくなりました.もうだめです.テキストの原型チェーンをひっくり返します.
    export default function isPlainObject(obj) {
      if (typeof obj !== 'object' || obj === null) return false
    
      let proto = obj
      while (Object.getPrototypeOf(proto) !== null) {
        proto = Object.getPrototypeOf(proto)
      }
    
      return Object.getPrototypeOf(obj) === proto
    }
    
    Redux git倉庫