成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

你想要的——redux源碼分析

enrecul101 / 1749人閱讀

摘要:大家好,今天給大家?guī)?lái)的是的源碼分析首先是的地址點(diǎn)我接下來(lái)我們看看在項(xiàng)目中的簡(jiǎn)單使用,一般我們都從最簡(jiǎn)單的開(kāi)始入手哈備注例子中結(jié)合的是進(jìn)行使用,當(dāng)然不僅僅能結(jié)合,還能結(jié)合市面上其他大多數(shù)的框架,這也是它比較流弊的地方首先是創(chuàng)建一個(gè)首先我們

大家好,今天給大家?guī)?lái)的是redux(v3.6.0)的源碼分析~

首先是redux的github地址 點(diǎn)我

接下來(lái)我們看看redux在項(xiàng)目中的簡(jiǎn)單使用,一般我們都從最簡(jiǎn)單的開(kāi)始入手哈

備注:例子中結(jié)合的是react進(jìn)行使用,當(dāng)然redux不僅僅能結(jié)合react,還能結(jié)合市面上其他大多數(shù)的框架,這也是它比較流弊的地方

首先是創(chuàng)建一個(gè)store

import React from "react"
import { render } from "react-dom"
// 首先我們必須先導(dǎo)入redux中的createStore方法,用于創(chuàng)建store
// 導(dǎo)入applyMiddleware方法,用于使用中間件
import { createStore, applyMiddleware } from "redux"
import { Provider } from "react-redux"
// 導(dǎo)入redux的中間件thunk
import thunk from "redux-thunk"
// 導(dǎo)入redux的中間件createLogger
import { createLogger } from "redux-logger"
// 我們還必須自己定義reducer函數(shù),用于根據(jù)我們傳入的action來(lái)訪問(wèn)新的state
import reducer from "./reducers"
import App from "./containers/App"

// 創(chuàng)建存放中間件數(shù)組
const middleware = [ thunk ]
if (process.env.NODE_ENV !== "production") {
  middleware.push(createLogger())
}
// 調(diào)用createStore方法來(lái)創(chuàng)建store,傳入的參數(shù)分別是reducer和運(yùn)用中間件的函數(shù)
const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)
// 將store作為屬性傳入,這樣在每個(gè)子組件中就都可以獲取這個(gè)store實(shí)例,然后使用store的方法
render(
  
    
  ,
  document.getElementById("root")
)

接下來(lái)我們看看reducer是怎么定義的

// 首先我們導(dǎo)入redux中的combineReducers方法
import { combineReducers } from "redux"
// 導(dǎo)入actions,這個(gè)非必須,但是推薦這么做
import {
  SELECT_REDDIT, INVALIDATE_REDDIT,
  REQUEST_POSTS, RECEIVE_POSTS
} from "../actions"

// 接下來(lái)這個(gè)兩個(gè)方法selectedReddit,postsByReddit就是reducer方法
// reducer方法負(fù)責(zé)根據(jù)傳入的action的類型,返回新的state,這里可以傳入默認(rèn)的state
const selectedReddit = (state = "reactjs", action) => {
  switch (action.type) {
    case SELECT_REDDIT:
      return action.reddit
    default:
      return state
  }
}

const posts = (state = {
  isFetching: false,
  didInvalidate: false,
  items: []
}, action) => {
  switch (action.type) {
    case INVALIDATE_REDDIT:
      return {
        ...state,
        didInvalidate: true
      }
    case REQUEST_POSTS:
      return {
        ...state,
        isFetching: true,
        didInvalidate: false
      }
    case RECEIVE_POSTS:
      return {
        ...state,
        isFetching: false,
        didInvalidate: false,
        items: action.posts,
        lastUpdated: action.receivedAt
      }
    default:
      return state
  }
}

const postsByReddit = (state = { }, action) => {
  switch (action.type) {
    case INVALIDATE_REDDIT:
    case RECEIVE_POSTS:
    case REQUEST_POSTS:
      return {
        ...state,
        [action.reddit]: posts(state[action.reddit], action)
      }
    default:
      return state
  }
}

// 最后我們通過(guò)combineReducers這個(gè)方法,將所有的reducer方法合并成一個(gè)方法,也就是rootReducer方法
const rootReducer = combineReducers({
  postsByReddit,
  selectedReddit
})
// 導(dǎo)出這個(gè)rootReducer方法
export default rootReducer

接下來(lái)看看action的定義,其實(shí)action就是一個(gè)對(duì)象,對(duì)象中約定有一個(gè)必要的屬性type,和一個(gè)非必要的屬性payload;type代表了action的類型,指明了這個(gè)action對(duì)state修改的意圖,而payload則是傳入一些額外的數(shù)據(jù)供reducer使用

export const REQUEST_POSTS = "REQUEST_POSTS"
export const RECEIVE_POSTS = "RECEIVE_POSTS"
export const SELECT_REDDIT = "SELECT_REDDIT"
export const INVALIDATE_REDDIT = "INVALIDATE_REDDIT"

export const selectReddit = reddit => ({
  type: SELECT_REDDIT,
  reddit
})
export const invalidateReddit = reddit => ({
  type: INVALIDATE_REDDIT,
  reddit
})

export const requestPosts = reddit => ({
  type: REQUEST_POSTS,
  reddit
})

export const receivePosts = (reddit, json) => ({
  type: RECEIVE_POSTS,
  reddit,
  posts: json.data.children.map(child => child.data),
  receivedAt: Date.now()
})

const fetchPosts = reddit => dispatch => {
  dispatch(requestPosts(reddit))
  return fetch(`https://www.reddit.com/r/${reddit}.json`)
    .then(response => response.json())
    .then(json => dispatch(receivePosts(reddit, json)))
}

const shouldFetchPosts = (state, reddit) => {
  const posts = state.postsByReddit[reddit]
  if (!posts) {
    return true
  }
  if (posts.isFetching) {
    return false
  }
  return posts.didInvalidate
}

export const fetchPostsIfNeeded = reddit => (dispatch, getState) => {
  if (shouldFetchPosts(getState(), reddit)) {
    return dispatch(fetchPosts(reddit))
  }
}

以上就是redux最簡(jiǎn)單的用法,接下來(lái)我們就來(lái)看看redux源碼里面具體是怎么實(shí)現(xiàn)的吧

首先我們看看整個(gè)redux項(xiàng)目的目錄結(jié)構(gòu),從目錄中我們可以看出,redux的項(xiàng)目源碼其實(shí)比較簡(jiǎn)單

接下來(lái)就從入口文件index.js開(kāi)始看吧,這個(gè)文件其實(shí)沒(méi)有實(shí)現(xiàn)什么實(shí)質(zhì)性的功能,只是導(dǎo)出了redux所提供的能力

// 入口文件
// 首先引入相應(yīng)的模塊,具體模塊的內(nèi)容后續(xù)會(huì)詳細(xì)分析
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"

/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== "production", warn the user.
*/
function isCrushed() {}

if (
  process.env.NODE_ENV !== "production" &&
  typeof isCrushed.name === "string" &&
  isCrushed.name !== "isCrushed"
) {
  warning(
    "You are currently using minified code outside of NODE_ENV === "production". " +
    "This means that you are running a slower development build of Redux. " +
    "You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify " +
    "or DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) " +
    "to ensure you have the correct code for your production build."
  )
}
// 導(dǎo)出相應(yīng)的功能
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}

緊接著,我們就來(lái)看看redux中一個(gè)重要的文件,createStore.js。這個(gè)文件用于創(chuàng)建store

// 創(chuàng)建store的文件,提供了redux中store的所有內(nèi)置的功能,也是redux中比較重要的一個(gè)文件

// 首先引入相應(yīng)的模塊
import isPlainObject from "lodash/isPlainObject"
import $$observable from "symbol-observable"

/**
 * These are private action types reserved by Redux.
 * For any unknown actions, you must return the current state.
 * If the current state is undefined, you must return the initial state.
 * Do not reference these action types directly in your code.
 */

 // 定義了有個(gè)內(nèi)部使用的ActionType
export const ActionTypes = {
  INIT: "@@redux/INIT"
}

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * @param {Function} [enhancer] The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */

// 導(dǎo)出創(chuàng)建store的方法
// 這個(gè)方法接收三個(gè)參數(shù),分別是 reducer,預(yù)先加載的state,以及功能增強(qiáng)函數(shù)enhancer
export default function createStore(reducer, preloadedState, enhancer) {
  // 調(diào)整參數(shù),如果沒(méi)有傳入預(yù)先加載的state,并且第二個(gè)參數(shù)是一個(gè)函數(shù)的話,則把第二個(gè)參數(shù)為功能增強(qiáng)函數(shù)enhancer
  if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
    enhancer = preloadedState
    preloadedState = undefined
  }
  // 判斷enhancer必須是一個(gè)函數(shù)
  if (typeof enhancer !== "undefined") {
    if (typeof enhancer !== "function") {
      throw new Error("Expected the enhancer to be a function.")
    }
    // 這是一個(gè)很重要的處理,它將createStore方法作為參數(shù)傳入enhancer函數(shù),并且執(zhí)行enhancer
    // 這里主要是提供給redux中間件的使用,以此來(lái)達(dá)到增強(qiáng)整個(gè)redux流程的效果
    // 通過(guò)這個(gè)函數(shù),也給redux提供了無(wú)限多的可能性
    return enhancer(createStore)(reducer, preloadedState)
  }
  // reducer必須是一個(gè)函數(shù),否則報(bào)錯(cuò)
  if (typeof reducer !== "function") {
    throw new Error("Expected the reducer to be a function.")
  }
  // 將傳入的reducer緩存到currentReducer變量中
  let currentReducer = reducer
  // 將傳入的preloadedState緩存到currentState變量中
  let currentState = preloadedState
  // 定義當(dāng)前的監(jiān)聽(tīng)者隊(duì)列
  let currentListeners = []
  // 定義下一個(gè)循環(huán)的監(jiān)聽(tīng)者隊(duì)列
  let nextListeners = currentListeners
  // 定義一個(gè)判斷是否在dispatch的標(biāo)志位
  let isDispatching = false

  // 判斷是否能執(zhí)行下一次監(jiān)聽(tīng)隊(duì)列
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      // 這里是將當(dāng)前監(jiān)聽(tīng)隊(duì)列通過(guò)拷貝的形式賦值給下次監(jiān)聽(tīng)隊(duì)列,這樣做是為了防止在當(dāng)前隊(duì)列執(zhí)行的時(shí)候會(huì)影響到自身,所以拷貝了一份副本
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * Reads the state tree managed by the store.
   *
   * @returns {any} The current state tree of your application.
   */
   // 獲取當(dāng)前的state
  function getState() {
    return currentState
  }

  /**
   * Adds a change listener. It will be called any time an action is dispatched,
   * and some part of the state tree may potentially have changed. You may then
   * call `getState()` to read the current state tree inside the callback.
   *
   * You may call `dispatch()` from a change listener, with the following
   * caveats:
   *
   * 1. The subscriptions are snapshotted just before every `dispatch()` call.
   * If you subscribe or unsubscribe while the listeners are being invoked, this
   * will not have any effect on the `dispatch()` that is currently in progress.
   * However, the next `dispatch()` call, whether nested or not, will use a more
   * recent snapshot of the subscription list.
   *
   * 2. The listener should not expect to see all state changes, as the state
   * might have been updated multiple times during a nested `dispatch()` before
   * the listener is called. It is, however, guaranteed that all subscribers
   * registered before the `dispatch()` started will be called with the latest
   * state by the time it exits.
   *
   * @param {Function} listener A callback to be invoked on every dispatch.
   * @returns {Function} A function to remove this change listener.
   */

  // 往監(jiān)聽(tīng)隊(duì)列里面去添加監(jiān)聽(tīng)者
  function subscribe(listener) {
    // 監(jiān)聽(tīng)者必須是一個(gè)函數(shù)
    if (typeof listener !== "function") {
      throw new Error("Expected listener to be a function.")
    }
    // 聲明一個(gè)變量來(lái)標(biāo)記是否已經(jīng)subscribed,通過(guò)閉包的形式被緩存
    let isSubscribed = true
    // 創(chuàng)建一個(gè)當(dāng)前currentListeners的副本,賦值給nextListeners
    ensureCanMutateNextListeners()
    // 將監(jiān)聽(tīng)者函數(shù)push到nextListeners中
    nextListeners.push(listener)
    // 返回一個(gè)取消監(jiān)聽(tīng)的函數(shù)
    // 原理很簡(jiǎn)單就是從將當(dāng)前函數(shù)從數(shù)組中刪除,使用的是數(shù)組的splice方法
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  /**
   * Dispatches an action. It is the only way to trigger a state change.
   *
   * The `reducer` function, used to create the store, will be called with the
   * current state tree and the given `action`. Its return value will
   * be considered the **next** state of the tree, and the change listeners
   * will be notified.
   *
   * The base implementation only supports plain object actions. If you want to
   * dispatch a Promise, an Observable, a thunk, or something else, you need to
   * wrap your store creating function into the corresponding middleware. For
   * example, see the documentation for the `redux-thunk` package. Even the
   * middleware will eventually dispatch plain object actions using this method.
   *
   * @param {Object} action A plain object representing “what changed”. It is
   * a good idea to keep actions serializable so you can record and replay user
   * sessions, or use the time travelling `redux-devtools`. An action must have
   * a `type` property which may not be `undefined`. It is a good idea to use
   * string constants for action types.
   *
   * @returns {Object} For convenience, the same action object you dispatched.
   *
   * Note that, if you use a custom middleware, it may wrap `dispatch()` to
   * return something else (for example, a Promise you can await).
   */

  // redux中通過(guò)dispatch一個(gè)action,來(lái)觸發(fā)對(duì)store中的state的修改
  // 參數(shù)就是一個(gè)action
  function dispatch(action) {
    // 這里判斷一下action是否是一個(gè)純對(duì)象,如果不是則拋出錯(cuò)誤
    if (!isPlainObject(action)) {
      throw new Error(
        "Actions must be plain objects. " +
        "Use custom middleware for async actions."
      )
    }
    // action中必須要有type屬性,否則拋出錯(cuò)誤
    if (typeof action.type === "undefined") {
      throw new Error(
        "Actions may not have an undefined "type" property. " +
        "Have you misspelled a constant?"
      )
    }
    // 如果上一次dispatch還沒(méi)結(jié)束,則不能繼續(xù)dispatch下一次
    if (isDispatching) {
      throw new Error("Reducers may not dispatch actions.")
    }

    try {
      // 將isDispatching設(shè)置為true,表示當(dāng)次dispatch開(kāi)始
      isDispatching = true
      // 利用傳入的reducer函數(shù)處理state和action,返回新的state
      // 推薦不直接修改原有的currentState
      currentState = currentReducer(currentState, action)
    } finally {
      // 當(dāng)次的dispatch結(jié)束
      isDispatching = false
    }
    // 每次dispatch結(jié)束之后,就執(zhí)行監(jiān)聽(tīng)隊(duì)列中的監(jiān)聽(tīng)函數(shù)
    // 將nextListeners賦值給currentListeners,保證下一次執(zhí)行ensureCanMutateNextListeners方法的時(shí)候會(huì)重新拷貝一個(gè)新的副本
    // 簡(jiǎn)單粗暴的使用for循環(huán)執(zhí)行
    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // 最后返回action
    return action
  }

  /**
   * Replaces the reducer currently used by the store to calculate the state.
   *
   * You might need this if your app implements code splitting and you want to
   * load some of the reducers dynamically. You might also need this if you
   * implement a hot reloading mechanism for Redux.
   *
   * @param {Function} nextReducer The reducer for the store to use instead.
   * @returns {void}
   */
  // replaceReducer方法,顧名思義就是替換當(dāng)前的reducer處理函數(shù)
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== "function") {
      throw new Error("Expected the nextReducer to be a function.")
    }

    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
  }

  /**
   * Interoperability point for observable/reactive libraries.
   * @returns {observable} A minimal observable of state changes.
   * For more information, see the observable proposal:
   * https://github.com/tc39/proposal-observable
   */
  // 這個(gè)函數(shù)一般來(lái)說(shuō)用不到,他是配合其他特點(diǎn)的框架或編程思想來(lái)使用的如rx.js,感興趣的朋友可以自行學(xué)習(xí)
  // 這里就不多做介紹
  function observable() {
    const outerSubscribe = subscribe
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      subscribe(observer) {
        if (typeof observer !== "object") {
          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
      }
    }
  }

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.

  // dispatch一個(gè)初始化的action
  dispatch({ type: ActionTypes.INIT })

  // 最后返回這個(gè)store的所有能力
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

接下來(lái)我們看看combineReducers.js這個(gè)文件,通常我們會(huì)用它來(lái)合并我們的reducer方法

這個(gè)文件用于合并多個(gè)reducer,然后返回一個(gè)根reducer

因?yàn)閟tore中只允許有一個(gè)reducer函數(shù),所以當(dāng)我們需要進(jìn)行模塊拆分的時(shí)候,就必須要用到這個(gè)方法

// 一開(kāi)始先導(dǎo)入相應(yīng)的函數(shù)
import { ActionTypes } from "./createStore"
import isPlainObject from "lodash/isPlainObject"
import warning from "./utils/warning"

// 獲取UndefinedState的錯(cuò)誤信息
function getUndefinedStateErrorMessage(key, action) {
  const actionType = action && action.type
  const actionName = (actionType && `"${actionType.toString()}"`) || "an action"

  return (
    `Given action ${actionName}, reducer "${key}" returned undefined. ` +
    `To ignore an action, you must explicitly return the previous state. ` +
    `If you want this reducer to hold no value, you can return null instead of undefined.`
  )
}

function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
  // 獲取reducers的所有key
  const reducerKeys = Object.keys(reducers)
  const argumentName = action && action.type === ActionTypes.INIT ?
    "preloadedState argument passed to createStore" :
    "previous state received by the reducer"
  // 當(dāng)reducers對(duì)象是一個(gè)空對(duì)象的話,返回警告文案
  if (reducerKeys.length === 0) {
    return (
      "Store does not have a valid reducer. Make sure the argument passed " +
      "to combineReducers is an object whose values are reducers."
    )
  }
  // state必須是一個(gè)對(duì)象
  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "` +
      ({}).toString.call(inputState).match(/s([a-z|A-Z]+)/)[1] +
      `". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join("", "")}"`
    )
  }
  // 判斷state中是否有reducer沒(méi)有的key,因?yàn)閞edux對(duì)state分模塊的時(shí)候,是依據(jù)reducer來(lái)劃分的
  const unexpectedKeys = Object.keys(inputState).filter(key =>
    !reducers.hasOwnProperty(key) &&
    !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? "keys" : "key"} ` +
      `"${unexpectedKeys.join("", "")}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join("", "")}". Unexpected keys will be ignored.`
    )
  }
}
// assertReducerShape函數(shù),檢測(cè)當(dāng)遇到位置action的時(shí)候,reducer是否會(huì)返回一個(gè)undefined,如果是的話則拋出錯(cuò)誤
// 接受一個(gè)reducers對(duì)象
function assertReducerShape(reducers) {
  // 遍歷這個(gè)reducers對(duì)象
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
    // 獲取reducer函數(shù)在處理當(dāng)state是undefined,actionType為初始默認(rèn)type的時(shí)候返回的值
    const initialState = reducer(undefined, { type: ActionTypes.INIT })
    // 如果這個(gè)值是undefined,則拋出錯(cuò)誤,因?yàn)槌跏約tate不應(yīng)該是undefined
    if (typeof initialState === "undefined") {
      throw new Error(
        `Reducer "${key}" returned undefined during initialization. ` +
        `If the state passed to the reducer is undefined, you must ` +
        `explicitly return the initial state. The initial state may ` +
        `not be undefined. If you don"t want to set a value for this reducer, ` +
        `you can use null instead of undefined.`
      )
    }
    // 當(dāng)遇到一個(gè)不知道的action的時(shí)候,reducer也不能返回undefined,否則也會(huì)拋出報(bào)錯(cuò)
    const type = "@@redux/PROBE_UNKNOWN_ACTION_" + Math.random().toString(36).substring(7).split("").join(".")
    if (typeof reducer(undefined, { type }) === "undefined") {
      throw new Error(
        `Reducer "${key}" returned undefined when probed with a random type. ` +
        `Don"t try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
        `namespace. They are considered private. Instead, you must return the ` +
        `current state for any unknown actions, unless it is undefined, ` +
        `in which case you must return the initial state, regardless of the ` +
        `action type. The initial state may not be undefined, but can be null.`
      )
    }
  })
}

/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @param {Object} reducers An object whose values correspond to different
 * reducer functions that need to be combined into one. One handy way to obtain
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 * undefined for any action. Instead, they should return their initial state
 * if the state passed to them was undefined, and the current state for any
 * unrecognized action.
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 */

// 導(dǎo)出combineReducers方法,接受一個(gè)參數(shù)reducers對(duì)象
export default function combineReducers(reducers) {
  // 獲取reducers對(duì)象的key值
  const reducerKeys = Object.keys(reducers)
  // 定義一個(gè)最終要返回的reducers對(duì)象
  const finalReducers = {}
  // 遍歷這個(gè)reducers對(duì)象的key
  for (let i = 0; i < reducerKeys.length; i++) {
    // 緩存每個(gè)key值
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== "production") {
      if (typeof reducers[key] === "undefined") {
        warning(`No reducer provided for key "${key}"`)
      }
    }
    // 相應(yīng)key的值是個(gè)函數(shù),則將改函數(shù)緩存到finalReducers中
    if (typeof reducers[key] === "function") {
      finalReducers[key] = reducers[key]
    }
  }
  // 獲取finalReducers的所有的key值,緩存到變量finalReducerKeys中
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== "production") {
    unexpectedKeyCache = {}
  }
  // 定義一個(gè)變量,用于緩存錯(cuò)誤對(duì)象
  let shapeAssertionError
  try {
    // 做錯(cuò)誤處理,詳情看后面assertReducerShape方法
    // 主要就是檢測(cè),
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(state = {}, action) {
    // 如果有錯(cuò)誤,則拋出錯(cuò)誤
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== "production") {
      // 獲取警告提示
      const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
      if (warningMessage) {
        warning(warningMessage)
      }
    }
    // 定義一個(gè)變量來(lái)表示state是否已經(jīng)被改變
    let hasChanged = false
    // 定義一個(gè)變量,來(lái)緩存改變后的state
    const nextState = {}
    // 開(kāi)始遍歷finalReducerKeys
    for (let i = 0; i < finalReducerKeys.length; i++) {
      // 獲取有效的reducer的key值
      const key = finalReducerKeys[i]
      // 根據(jù)key值獲取對(duì)應(yīng)的reducer函數(shù)
      const reducer = finalReducers[key]
      // 根據(jù)key值獲取對(duì)應(yīng)的state模塊
      const previousStateForKey = state[key]
      // 執(zhí)行reducer函數(shù),獲取相應(yīng)模塊的state
      const nextStateForKey = reducer(previousStateForKey, action)
      // 如果獲取的state是undefined,則拋出錯(cuò)誤
      if (typeof nextStateForKey === "undefined") {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 將獲取到的新的state賦值給新的state對(duì)應(yīng)的模塊,key則為當(dāng)前reducer的key
      nextState[key] = nextStateForKey
      // 判讀state是否發(fā)生改變
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 如果state發(fā)生改變則返回新的state,否則返回原來(lái)的state
    return hasChanged ? nextState : state
  }
}

接下來(lái)我們?cè)诳纯碽indActionCreators.js這個(gè)文件

首先先認(rèn)識(shí)actionCreators,簡(jiǎn)單來(lái)說(shuō)就是創(chuàng)建action的方法,redux的action是一個(gè)對(duì)象,而我們經(jīng)常使用一些函數(shù)來(lái)創(chuàng)建這些對(duì)象,則這些函數(shù)就是actionCreators

而這個(gè)文件實(shí)現(xiàn)的功能,是根據(jù)綁定的actionCreator,來(lái)實(shí)現(xiàn)自動(dòng)dispatch的功能

import warning from "./utils/warning"
// 對(duì)于每個(gè)actionCreator方法,執(zhí)行之后都會(huì)得到一個(gè)action
// 這個(gè)bindActionCreator方法,會(huì)返回一個(gè)能夠自動(dòng)執(zhí)行dispatch的方法
function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

/**
 * Turns an object whose values are action creators, into an object with the
 * same keys, but with every function wrapped into a `dispatch` call so they
 * may be invoked directly. This is just a convenience method, as you can call
 * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
 *
 * For convenience, you can also pass a single function as the first argument,
 * and get a function in return.
 *
 * @param {Function|Object} actionCreators An object whose values are action
 * creator functions. One handy way to obtain it is to use ES6 `import * as`
 * syntax. You may also pass a single function.
 *
 * @param {Function} dispatch The `dispatch` function available on your Redux
 * store.
 *
 * @returns {Function|Object} The object mimicking the original object, but with
 * every action creator wrapped into the `dispatch` call. If you passed a
 * function as `actionCreators`, the return value will also be a single
 * function.
 */
// 對(duì)外暴露這個(gè)bindActionCreators方法
export default function bindActionCreators(actionCreators, dispatch) {
  // 如果傳入的actionCreators參數(shù)是個(gè)函數(shù),則直接調(diào)用bindActionCreator方法
  if (typeof actionCreators === "function") {
    return bindActionCreator(actionCreators, dispatch)
  }
  // 錯(cuò)誤處理
  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是一個(gè)對(duì)象,則獲取對(duì)象中的key
  const keys = Object.keys(actionCreators)
  // 定義一個(gè)緩存對(duì)象
  const boundActionCreators = {}
  // 遍歷actionCreators的每個(gè)key
  for (let i = 0; i < keys.length; i++) {
    // 獲取每個(gè)key
    const key = keys[i]
    // 根據(jù)每個(gè)key獲取特定的actionCreator方法
    const actionCreator = actionCreators[key]
    // 如果actionCreator是一個(gè)函數(shù),則直接調(diào)用bindActionCreator方法,將返回的匿名函數(shù)緩存到boundActionCreators對(duì)象中
    if (typeof actionCreator === "function") {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    } else {
      warning(`bindActionCreators expected a function actionCreator for key "${key}", instead received type "${typeof actionCreator}".`)
    }
  }
  // 最后返回boundActionCreators對(duì)象
  // 用戶獲取到這個(gè)對(duì)象后,可拿出對(duì)象中的每個(gè)key的對(duì)應(yīng)的值,也就是各個(gè)匿名函數(shù),執(zhí)行匿名函數(shù)就可以實(shí)現(xiàn)dispatch功能
  return boundActionCreators
}

接下來(lái)我們看看applyMiddleware.js這個(gè)文件,這個(gè)文件讓redux有著無(wú)限多的可能性。為什么這么說(shuō)呢,你往下看就知道了

// 這個(gè)文件的代碼邏輯其實(shí)很簡(jiǎn)單
// 首先導(dǎo)入compose函數(shù),等一下我們會(huì)詳細(xì)分析這個(gè)compose函數(shù)
import compose from "./compose"

/**
 * Creates a store enhancer that applies middleware to the dispatch method
 * of the Redux store. This is handy for a variety of tasks, such as expressing
 * asynchronous actions in a concise manner, or logging every action payload.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
 // 接下來(lái)導(dǎo)出applyMiddleware這個(gè)方法,這個(gè)方法也是我們經(jīng)常用來(lái)作為createStore中enhance參數(shù)的一個(gè)方法
export default function applyMiddleware(...middlewares) {
  // 首先先返回一個(gè)匿名函數(shù),有沒(méi)有發(fā)現(xiàn)這個(gè)函數(shù)跟createStore很相似啊
  // 沒(méi)錯(cuò)其實(shí)他就是我們的之前看到的createStore
  return (createStore) => (reducer, preloadedState, enhancer) => {
    // 首先用原來(lái)的createStore創(chuàng)建一個(gè)store,并把它緩存起來(lái)
    const store = createStore(reducer, preloadedState, enhancer)
    // 獲取store中原始的dispatch方法
    let dispatch = store.dispatch
    // 定一個(gè)執(zhí)行鏈數(shù)組
    let chain = []
    // 緩存原有store中g(shù)etState和dispatch方法
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    // 執(zhí)行每個(gè)中間件函數(shù),并將middlewareAPI作為參數(shù)傳入,獲得一個(gè)執(zhí)行鏈數(shù)組
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 將執(zhí)行鏈數(shù)組傳入compose方法中,并立即執(zhí)行返回的方法獲得最后包裝過(guò)后的dispatch
    // 這個(gè)過(guò)程簡(jiǎn)單來(lái)說(shuō)就是,每個(gè)中間件都會(huì)接受一個(gè)store.dispatch方法,然后基于這個(gè)方法進(jìn)行包裝,然后返回一個(gè)新的dispatch
    // 這個(gè)新的dispatch又作為參數(shù)傳入下一個(gè)中間件函數(shù),然后有進(jìn)行包裝。。。一直循環(huán)這個(gè)過(guò)程,直到最后得到一個(gè)最終的dispatch
    dispatch = compose(...chain)(store.dispatch)
    // 返回一個(gè)store對(duì)象,并將新的dispatch方法覆蓋原有的dispatch方法
    return {
      ...store,
      dispatch
    }
  }
}

看到這里,其實(shí)你已經(jīng)看完了大部分redux的內(nèi)容,最后我們看看上述文件中使用到的compose方法是如何實(shí)現(xiàn)的。

打開(kāi)compose.js,我們發(fā)現(xiàn)其實(shí)實(shí)現(xiàn)方式就是利用es5中數(shù)組的reduce方法來(lái)實(shí)現(xiàn)這種效果的

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */

export default function compose(...funcs) {
  // 判斷函數(shù)數(shù)組是否為空
  if (funcs.length === 0) {
    return arg => arg
  }
  // 如果函數(shù)數(shù)組只有一個(gè)元素,則直接執(zhí)行
  if (funcs.length === 1) {
    return funcs[0]
  }

  // 否則,就利用reduce方法執(zhí)行每個(gè)中間件函數(shù),并將上一個(gè)函數(shù)的返回作為下一個(gè)函數(shù)的參數(shù)
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

哈哈,以上就是今天給大家分享的redux源碼分析~希望大家能夠喜歡咯

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84229.html

相關(guān)文章

  • 史上最全 Redux 源碼分析

    摘要:訂閱器不應(yīng)該關(guān)注所有的變化,在訂閱器被調(diào)用之前,往往由于嵌套的導(dǎo)致發(fā)生多次的改變,我們應(yīng)該保證所有的監(jiān)聽(tīng)都注冊(cè)在之前。 前言 用 React + Redux 已經(jīng)一段時(shí)間了,記得剛開(kāi)始用Redux 的時(shí)候感覺(jué)非常繞,總搞不起里面的關(guān)系,如果大家用一段時(shí)間Redux又看了它的源碼話,對(duì)你的理解會(huì)有很大的幫助??赐旰?,在回來(lái)看Redux,有一種 柳暗花明又一村 的感覺(jué) ,. 源碼 我分析的...

    fai1017 評(píng)論0 收藏0
  • Redux原理分析

    摘要:調(diào)用鏈中最后一個(gè)會(huì)接受真實(shí)的的方法作為參數(shù),并借此結(jié)束調(diào)用鏈??偨Y(jié)我們常用的一般是除了和之外的方法,那個(gè)理解明白了,對(duì)于以后出現(xiàn)的問(wèn)題會(huì)有很大幫助,本文只是針對(duì)最基礎(chǔ)的進(jìn)行解析,之后有機(jī)會(huì)繼續(xù)解析對(duì)他的封裝 前言 雖然一直使用redux+react-redux,但是并沒(méi)有真正去講redux最基礎(chǔ)的部分理解透徹,我覺(jué)得理解明白redux會(huì)對(duì)react-redux有一個(gè)透徹的理解。 其實(shí),...

    sumory 評(píng)論0 收藏0
  • 函數(shù)柯里化與Redux中間件及applyMiddleware源碼分析

    摘要:函數(shù)的柯里化的基本使用方法和函數(shù)綁定是一樣的使用一個(gè)閉包返回一個(gè)函數(shù)。先來(lái)一段我自己實(shí)現(xiàn)的函數(shù)高程里面這么評(píng)價(jià)它們兩個(gè)的方法也實(shí)現(xiàn)了函數(shù)的柯里化。使用還是要根據(jù)是否需要對(duì)象響應(yīng)來(lái)決定。 奇怪,怎么把函數(shù)的柯里化和Redux中間件這兩個(gè)八竿子打不著的東西聯(lián)系到了一起,如果你和我有同樣疑問(wèn)的話,說(shuō)明你對(duì)Redux中間件的原理根本就不了解,我們先來(lái)講下什么是函數(shù)的柯里化?再來(lái)講下Redux的...

    jeyhan 評(píng)論0 收藏0
  • redux深入進(jìn)階

    摘要:上一篇文章講解了如何使用,本篇文章將進(jìn)一步深入,從的源碼入手,深入學(xué)習(xí)的中間件機(jī)制。的功能是讓支持異步,讓我們可以在中跟服務(wù)器進(jìn)行交互等操作,而他的實(shí)現(xiàn)。。。 上一篇文章講解了redux如何使用,本篇文章將進(jìn)一步深入,從redux的源碼入手,深入學(xué)習(xí)redux的中間件機(jī)制。在這里我們會(huì)以一個(gè)redux-thunk中間件為例,逐步分解redux的中間機(jī)制如何操作,如何執(zhí)行。 閑話不多說(shuō),...

    omgdog 評(píng)論0 收藏0
  • 深入redux技術(shù)棧

    摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽(tīng),完成整個(gè)狀態(tài)樹(shù)的更新??偠灾?,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...

    imingyu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<