Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redux源码分析(一) #169

Closed
Rashomon511 opened this issue Apr 15, 2019 · 0 comments
Closed

redux源码分析(一) #169

Rashomon511 opened this issue Apr 15, 2019 · 0 comments

Comments

@Rashomon511
Copy link
Owner

Rashomon511 commented Apr 15, 2019

本人从毕业就开始使用react+redux这一套框架,虽然已经使用一年多,但是这之前都没有阅读redux源码,希望这次的源码阅读与分析可以让我对redux理解更深刻

  • 对于redux的工作流程不了解的同学可以看这篇文章

redux的源码并不是很多,大致只分为下列几个文件:

image

image

index.js是整个代码的入口,主要是判断代码在非生成环境是否压缩暴露出createStore,combineReducers,bindActionCreators,applyMiddleware等几个方法,如果没有这发出警告⚠️

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'

/*
 * 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 setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
      'to ensure you have the correct code for your production build.'
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}

createStore.js

createStore是redux中一个非常重要的API,createStore会生成一个store,维护一个状态树,里面是全局的state,

store = createStore(reducer, preloadedState, enhancer)

createStore接受三个参数,分别为reducer纯函数,初始状态,增强器(即 applyMiddleware()返回的东西以及其他的中间件)用法如下:

const enhancer = compose(
    applyMiddleware(sagaMiddleware),
    composeWithDevTools()
);

createStore生成的store提供了dispath,subscribe,getState,replaceReducer,observable等方法,接下来逐一分析。

export default function createStore(reducer, preloadedState, enhancer) {
  // 判断接受的参数个数,来指定 reducer 、 preloadedState 和 enhancer
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  let currentReducer = reducer   // 存储当前的reducer
  let currentState = preloadedState  // 存储当前的状态
  let currentListeners = []     // 储存当前的监听函数列表
  let nextListeners = currentListeners  // 储存下一个监听函数列表
  let isDispatching = false  //是否在执行reducer
  }

首先判定参数个数,分别指定三个参数,接着判断enhancer是否存在并且为函数则调用enhancer,并且终止当前函数执行,前面的判断基本上是对三个参数对判断。接下来是对当前状态及reducer的存储。

getState

  // 接上述代码,这里不在重复
  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
  }

这里判断是否在执行reducer,返回当前的最新状态state.

dispatch

  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    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
  }

dispatch接受一个参数action,action 是把数据从应用传到 store 的有效载荷,它是 store 数据的唯一来源,action包含type和payload,payload表示最新的状态state,type代表操作类型。传入action后会首先运行reducer,reducer接收action和当前状态state,reducer只判断type属性来返回最新的state。
dispatch还会触发整个监听函数列表,所以最后整个监听函数列表都会按顺序执行一遍。dispatch返回值就是传入的action。

subscribe

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

ensureCanMutateNextListeners 会根据当前的监听函数列表生成下一个监听函数列表。

  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
      )
    }

    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)
    }
  }

subscribe会接受一个listener函数,首先运行ensureCanMutateNextListeners生成下一个监听列表,然后向列表中添加进新的监听函数listenner,subscribe的返回值是一个unsubscribe,是一个解绑函数,调用该解绑函数,会将已经添加的监听函数删除,该监听函数处于一个闭包之中,会一直存在,所以在解绑函数中能删除该监听函数。这就是redux精妙之处。

replaceReducer

  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

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

顾名思义replaceReducer的意思就是替换掉reducer,replaceReducer接受一个reducer替换的当前的reducer,之后立即执行dispatch({ type: ActionTypes.INIT }) ,用来初始化store的状态。
replaceReducer的使用场景,分别是:

1.当你的程序要进行代码分割的时候
2.当你要动态的加载不同的reducer的时候
3.当你要实现一个实时reloading机制的时候

observable

该方法并不是暴露给使用者的,一般用于内部,在测试的时候会用到,这里不深究。

redux源码分析(二)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant