soulcm / blog

2017开通
6 stars 0 forks source link

redux源码分析(一) #7

Open soulcm opened 7 years ago

soulcm commented 7 years ago

redux源码分析(一)

现如今前端应用已经是数据驱动试图的时代,但当应用越来越大,越来越复杂时,其数据状态的管理就变得至关重要,基于facebook的flux状态管理框架,redux也应运而生了,其核心思想就是如下几点

1、应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。

2、惟一改变 state 的办法是触发 action,一个描述发生什么的对象。

3、为了描述 action 如何改变 state 树,你需要编写 reducers。

针对这几条核心思想,来统一管理一个store,redux的源码实现也非常的简介清晰,下面将结合自己的阅读对其源码进行一定的分析。先来看看整个src目录

|--src
    |--utils
       |--warning.js
    |--applyMiddleware.js
    |--bindActionCreators.js
    |--combineReducers.js 
    |--compose.js //一个组合函数
    |--createStore.js  //store的创建,核心
    |--index.js //对外提供的接口

所有代码加起来粗略估算还不超过700行,可以看出redux的实现非常简介,其思想有很多值得我们借鉴的地方。

首先来看最核心的createStore.js文件,它直接对外抛出了一个创建store的createStore函数供外部调用,其接收一个纯函数reducer,一个初始的state,以及一个可选的增强函数enhancer

export default function createStore(reducer, preloadedState, enhancer) {
    //code
}

此篇先来看看没有enhancer的情况(enhancer的情况将放在后面介绍),按照redux的核心,要改变state,必须dispatch一个action,那就先来看看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
    }

    var listeners = currentListeners = nextListeners
    for (var i = 0; i < listeners.length; i++) {
      listeners[i]()
    }
    return action
  }

刚开始对传入的action进行了校验,action必须是一个对象,且按约定,应该包含一个type属性。然后currentState = currentReducer(currentState, action)就是纯函数reducer的执行,其接收一个当前的state和这个action,根据action的type来进行分发,返回一个新的state,如果未匹配到,就返回初始的state。一个基本的reducer应该是这样

export default function(state={}, action) {
    switch(action.type) {
        case 'A':
            return Object.assign({}, state, {a: 2})
        default:
            return state
    }
}

接着可以看到会遍历当前的listener并执行,这些listener是在subscribe订阅函数中注册的,每当dispatch一个action都会执行这些listener,源码如下,其将返回一个取消订阅的函数,可以随时取消订阅。

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

    var isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

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

整个store暴露出了几个接口,以供调用

return {
    dispatch, 
    subscribe, //订阅listener
    getState, //获取currenState
    replaceReducer,  //用新的reducer替换currentReducer
    [$$observable]: observable
}

下篇将介绍bindActionCreators