当前位置: 移动技术网 > IT编程>网页制作>CSS > redux源码解析实例教程

redux源码解析实例教程

2018年10月07日  | 移动技术网IT编程  | 我要评论
redux 解析 目录结构 ├── applymiddleware.js // 使用中间件模块,有了它就可以组合中间件一起使用 ├── bindactioncreators.j

redux 解析

目录结构

├── applymiddleware.js         // 使用中间件模块,有了它就可以组合中间件一起使用
├── bindactioncreators.js
├── combinereducers.js
├── compose.js                 // 组成函数,用于把函数列表组合成一个嵌套执行的函数
├── createstore.js
├── index.js                   // 主函数,用于导出库的 api 函数
└── utils                      // 工具文件夹,用于存放一些工具函数
    ├── actiontypes.js
    ├── isplainobject.js
    └── warning.js

index.js

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'

/*
 * 这是一个空函数,用于检查当前代码是否被压缩,如果当前代码是压缩后的,
 * 且当前环境不为 production 那么那么警告用户
 */
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
}

工具函数

我们可以看出, redux 引用了三个工具函数文件,且这三个工具函数是不对外暴露的,那么这三个工具函数文件中分别有什么内容呢?现在我们来简单的解析一下

actiontype.js

/*
 * 一个私有的函数,用于生成一个随机字符串
 * 使用 tostring(36) 就是利用 tostring 的特性将其转化成 36 进制的, 为 0-9, a-z
 * 然后从 substring(7) 从第 7 位开始取内容,这样就可以取一个有效的,不含 0. 且位数足够长也足够短的字符串
 */
const randomstring = () =>
  math.random()
    .tostring(36)
    .substring(7)
    .split('')
    .join('.')

/*
 * 生成一个 actiontypes 类型,就是一个单纯的对象
 */
const actiontypes = {
  init: `@@redux/init${randomstring()}`,
  replace: `@@redux/replace${randomstring()}`,
  probe_unknown_action: () => `@@redux/probe_unknown_action${randomstring()}`
}

export default actiontypes

isplainobject.js

/*
 * 用于检查传入的参数是不是一个简单的对象
 * 主要是用于检查这个对象是不是被继承
 */
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
}

warning.js

/*
 * 导出一个函数用于输出警告信息
 */
export default function warning(message) {
  // 如果 console 对象存在,且有 error 方法,那么就用它输出错误信息
  // 之所以这么写,是因为某些情况化,会重新定义 console 以防止别人调试代码
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    console.error(message)
  }
    
  try {
    // 抛出异常
    throw new error(message)
  } catch (e) {}
}

compose.js

/*
 * 组成函数,用于把函数列表组合成一个嵌套执行的函数
 */
export default function compose(...funcs) {
  // 如果没有传入参数,那么直接返回一个无用函数
  // function (arg) { return arg }
  if (funcs.length === 0) {
    return arg => arg
  }

  // 如果只传入了一个参数,那么就返回第一个函数
  if (funcs.length === 1) {
    return funcs[0]
  }

  // 如果传入的函数列表有很多项目,那么就组全成一个嵌套执行的函数,并返回
  // 比如传入的的是  compose(a, b, c, d, e)
  // 那么返回 a(b(c(d(e(...args))))), 在函数外只有 let func = compose(a,b,c,d,e) func(12,13) 就可以调用执行了
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

applymiddleware.js

import compose from './compose'

/*
 * 导出一个函数,用于使用中间件模块,有了它就可以组合中间件一起使用
 */
export default function applymiddleware(...middlewares) {
  // 创建一个 store
  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.`
      )
    }

    // 给中间件提供了 store.getstate 方法与 dispatch 方法,相当于提供了一个简易的 store
    const middlewareapi = {
      getstate: store.getstate,
      dispatch: (...args) => dispatch(...args)
    }
    // 挨个遍历执行中间件
    const chain = middlewares.map(middleware => middleware(middlewareapi))
    // 把多个中间件组合起来, 如果存在 fn1, fn2, fn3, fn4 那么 dispatch 就会变成
    // dispatch = fn1(fn2(fn3(fn4(store.dispatch))))
    dispatch = compose(...chain)(store.dispatch)

    // 返回中间件处理过的 store, dispatch
    return {
      ...store,
      dispatch
    }
  }
}

bindactioncreators.js

/*
 * 返回一个 dispatch 函数,这个函数是绑定了 this 对象的。 this 指向其初始传入的对象
 */
function bindactioncreator(actioncreator, dispatch) {
  return function() {
    return dispatch(actioncreator.apply(this, arguments))
  }
}

/*
 * 导出一个工具函数,用于将 dispatch 绑定 this 上下文
 */
export default function bindactioncreators(actioncreators, dispatch) {
  // 如果传入的是一个函数,一般情况就是用 function 模拟的 class, 这个时候直接返回绑定了上下文的函数
  if (typeof actioncreators === 'function') {
    return bindactioncreator(actioncreators, dispatch)
  }
 
  // 如果传入的不是一个对象,那么抛出异常
  // 私以为判断函数可以这么写 object.prototype.tostring.call(actioncreators) !== '[object object]' 比较好
  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"`
    )
  }

  // 如果传入的是一个对象,那么遍历对象的属性,对对象中每一个是 function 的属性进行绑定,然后返回
  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
}

combinereducers.js

import actiontypes from './utils/actiontypes'
import warning from './utils/warning'
import isplainobject from './utils/isplainobject'

// 如果要从 state 中出数据,但是 state 中是不存在该属性的,那么组合一个对应的错误信息
// 告诉用户在哪一个 action 中,取什么类型的数据错误
function getundefinedstateerrormessage(key, action) {
  const actiontype = action && action.type
  const actiondescription =
    (actiontype && `action "${string(actiontype)}"`) || 'an action'

  return (
    `given ${actiondescription}, 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
) {
  const reducerkeys = object.keys(reducers)
  // 检查是 store 初始的时候形成的还是收到 action 时形成
  const argumentname =
    action && action.type === actiontypes.init
       'preloadedstate argument passed to createstore'
      : 'previous state received by the reducer'

  // 如果没有传入 reducer 信息,那么直接返回错误信息,提示用户需要信息
  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 不是一个简单对象,那么返回错误信息,告知用户 state 应为简单对象
  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('", "')}"`
    )
  }

  // 先收集未预料的 key。这些 key 不存在于 reducers 中也不存在于 unexpectedkeycache 中
  const unexpectedkeys = object.keys(inputstate).filter(
    key => !reducers.hasownproperty(key) && !unexpectedkeycache[key]
  )

  // 把所有的未预料 key 放入缓存中
  unexpectedkeys.foreach(key => {
    unexpectedkeycache[key] = true
  })

  // 如果当前 action 类型是 replace 那么洗洗睡吧,啥都不干了
  if (action && action.type === actiontypes.replace) return

  // 如果存在未预料的 key 那么组装错误信息并返回
  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.`
    )
  }
}

// 断言 reducer 模型
function assertreducershape(reducers) {
  // 对传入的 reducers 列表中的每一个 reducer 来进行断言操作
  object.keys(reducers).foreach(key => {
    const reducer = reducers[key]
    const initialstate = reducer(undefined, { type: actiontypes.init })

    // 如果初始 state 为 undefined ,那么抛出异常,因为初始 state 要为对象类型
    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.`
      )
    }

    // 这里用于检测 action 的 type 处理是否有 default 如果没有,那么也会抛出一个异常,因为 reducer 至少返回一个稳定的 state
    if (
      typeof reducer(undefined, {
        type: actiontypes.probe_unknown_action()
      }) === '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.`
      )
    }
  })
}

/**
 * 导出一个公共函数,用于把传入的多个 reducer 对象组合成一个对象,以确保在 reducer 分门别类的同时 store 唯一
 */
export default function combinereducers(reducers) {
  const reducerkeys = object.keys(reducers)
  const finalreducers = {}
  for (let i = 0; i < reducerkeys.length; i++) {
    const key = reducerkeys[i]

    // 如果当前是开发环境,且引用了没有定义好的 action ,那么报出警告
    if (process.env.node_env !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`no reducer provided for key "${key}"`)
      }
    }

    // 如果 reducers[key] 是一个 function 类型的,那么说明它是最终的 reducer ,将它放入结果对象中
    if (typeof reducers[key] === 'function') {
      finalreducers[key] = reducers[key]
    }
  }
  const finalreducerkeys = object.keys(finalreducers)

  let unexpectedkeycache
  if (process.env.node_env !== 'production') {
    unexpectedkeycache = {}
  }

  // 断言最终的结果对象是一个 reducer
  let shapeassertionerror
  try {
    assertreducershape(finalreducers)
  } catch (e) {
    shapeassertionerror = e
  }

  // 返回结合函数,这个函数其实也就是最终的 reducer
  return function combination(state = {}, action) {
    // 如果结果对象不是一个 reducer 那么抛出异常
    if (shapeassertionerror) {
      throw shapeassertionerror
    }

    // 如果当前是开发环境,那么获取意外状态下的错误形成信息,有则改之,无则加勉
    if (process.env.node_env !== 'production') {
      const warningmessage = getunexpectedstateshapewarningmessage(
        state,
        finalreducers,
        action,
        unexpectedkeycache
      )
      if (warningmessage) {
        warning(warningmessage)
      }
    }

    let haschanged = false
    const nextstate = {}
    // 每次调用 reducers 的时候遍历执行,如果有变化,就返回新值,否则就返回原值
    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
  }
}

createstore.js

import $$observable from 'symbol-observable'

import actiontypes from './utils/actiontypes'
import isplainobject from './utils/isplainobject'

/*
 * 导出一个共工的 createstore 方法,用于创建唯一的 store
 */
export default function createstore(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)
  }

  // 检查 reducer 的类型
  if (typeof reducer !== 'function') {
    throw new error('expected the reducer to be a function.')
  }

  let currentreducer = reducer
  let currentstate = preloadedstate
  let currentlisteners = []
  let nextlisteners = currentlisteners
  let isdispatching = false

  // 工具函数,用于检查观察者可用
  function ensurecanmutatenextlisteners() {
    if (nextlisteners === currentlisteners) {
      nextlisteners = currentlisteners.slice()
    }
  }

  /*
   * 获取 state 内容,如果当前正在操作,那么抛出异常
   */
  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
  }

  /*
   * 注册观察者
   */
  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)
    }
  }

  /*
   * 分发 action
   */
  function dispatch(action) {
    // 检查 action 类型是否为简单对象
    if (!isplainobject(action)) {
      throw new error(
        'actions must be plain objects. ' +
          'use custom middleware for async actions.'
      )
    }

    // 检查 action.type 是否正确传入
    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.')
    }

    // 执行 reducer 里面的 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
  }

  /*
   * 更换 reducer
   */
  function replacereducer(nextreducer) {
    if (typeof nextreducer !== 'function') {
      throw new error('expected the nextreducer to be a function.')
    }

    currentreducer = nextreducer
    dispatch({ type: actiontypes.replace })
  }

  /*
   * 可监听的,其实就是观察者模式的一种实现方式
   */
  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
      }
    }
  }

  // 初始化
  dispatch({ type: actiontypes.init })

  return {
    dispatch,
    subscribe,
    getstate,
    replacereducer,
    [$$observable]: observable
  }
}

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网