Redux原理

注:本文适合有使用过Redux或者有了解过的童鞋阅读~

接触过的项目很多都用到了Redux,在今天之前,我只是会使用这个工具来进行数据的存储与处理,觉得这真是个好东西,毕竟在一个大的项目中,单单靠props、state、ref来传递数据还是不够的。
面试的时候也被问到了Redux的原理,但是只答了它的用法,没答上它的原理。学习不仅做到知其然,还要做到知其所以然。今天抽了个时间去了解了一下,其实也就那么回事吧~哈哈哈

Redux基本思想

保证数据的单向流动,同时便于控制、使用、测试。

Redux核心概念

  • action
    只是描述要发生的事件,并不改变state

  • reducer
    根据action的描述,改变state

  • dispatch
    将要发生的事件,分发到reducer,触发改变。store.dispatch(action)

  • store
    用来保存state;
    提供 getState() 方法获取 state;
    提供 dispatch(action) 方法更新 state;
    通过 subscribe(listener) 注册监听器;
    通过 subscribe(listener) 返回的函数注销监听器。

Redux 原理实现

store的创建

var createStore = require('redux').createStore;
var store = createStore(count);//count是个reducer

先看看createStore都返回了什么?

export default function createStore(reducer, initialState) {
  ...
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer
  }
}

可以看到,createStore接受两个参数:改变state的reducer和初始state

每个属性的含义是:

  • dispatch: 用于action的分发,改变store里面的state
  • subscribe: 注册listener,store里面state发生改变后,执行该listener
  • getState: 读取store里面的state
  • replaceReducer: 替换reducer,改变state修改的逻辑

createStore关键代码解析

export default function createStore(reducer, initialState) {
  var currentReducer = reducer
  var currentState = initialState
  var listeners = []
  var isDispatching = false;

  // 返回当前的state
  function getState() {
    return currentState
  }

  // 注册listener,同时返回一个取消事件注册的方法
  function subscribe(listener) {
    listeners.push(listener)
    var isSubscribed = true

    return function unsubscribe() {
      if (!isSubscribed) {
         return
      }
      isSubscribed = false
      var index = listeners.indexOf(listener)
      listeners.splice(index, 1)
    }
  }
  // 通过action该改变state,然后执行subscribe注册的方法
  function dispatch(action) {
    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    listeners.slice().forEach(listener => listener())
    return action
  }

  // 替换reducer,修改state变化的逻辑
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.INIT })
  }
  // 初始化时,执行内部一个dispatch,得到初始state
  dispatch({ type: ActionTypes.INIT })
}

保证store的唯一性

为每个reducer创建一个store,后期维护比较麻烦,通过combineReducers将多个reducer合并成一个rootReducer:
用法:

var combineReducers = require('redux').combineReducers;
var rootReducer = combineReducers({
  count: count,
  year: year,
});
// 创建store
var createStore = require('redux').createStore;
var store = createStore(rootReducer);

combineReducers源码分析:

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  //将合法的reducer提出来
  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)
  //返回一个reducer,同样接受两个参数,state和action
  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)    
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

与react框架的结合

利用react-redux工具实现二者的结合
用法:

var Provider = require('react-redux').Provider;
// App 为上层的Component
class App extend React.Component{
  render() {
    return (
      <Provier store={store}>
        <MyComponent />
      </Provider>
    );
  }
}

var connect = require('react-redux').connect;
var actionCreators = require('...');
// MyComponent是与redux无关的组件
var MyComponent = require('...');

function select(state) {
  return {
    count: state.count
  }
}
export default connect(select, actionCreators)(MyComponent)

Provider原理

React通过Context属性,可以将属性(props)直接给子孙component,无须通过props层层传递, Provider仅仅起到获得store,然后将其传递给子孙元素而已:

export default class Provider extends Component {
  getChildContext() { // getChildContext: 将store传递给子孙component
    return { store: this.store }
  }

  constructor(props, context) {
    super(props, context)
    this.store = props.store
  }

  componentWillReceiveProps(nextProps) {
    const { store } = this
    const { store: nextStore } = nextProps

    if (store !== nextStore) {
      warnAboutReceivingStore()
    }
  }

  render() {
    let { children } = this.props
    return Children.only(children)
  }
}

connect原理

connect函数接受四个参数,分别是:

  • mapStateToProps(state,ownProps):指定如何把当前Redux store state映射到展示组件的props中
  • mapDispatchToProps(dispatch,ownProps):接受dispatch方法并返回期望注入到展示组件的props中的回调方法
  • mergeProps:
  • options:

connect函数返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入,这样就生产出一个经过包裹的Connect组件,该组件通过this.context获取祖先Component的store,props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  return function wrapWithConnect(WrappedComponent) {
    class Connect extends Component {
      constructor(props, context) {
        // 从祖先Component处获得store
        this.store = props.store || context.store
        this.stateProps = computeStateProps(this.store, props)
        this.dispatchProps = computeDispatchProps(this.store, props)
        this.state = { storeState: null }
        // 对stateProps、dispatchProps、parentProps进行合并
        this.updateState()
      }
      shouldComponentUpdate(nextProps, nextState) {
        // 进行判断,当数据发生改变时,Component重新渲染
        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
          this.updateState(nextProps)
            return true
          }
        }
        componentDidMount() {
          // 改变Component的state
          this.store.subscribe(() = {
            this.setState({
              storeState: this.store.getState()
            })
          })
        }
        render() {
          // 生成包裹组件Connect
          return (
            <WrappedComponent {...this.nextState} />
          )
        }
      }
      Connect.contextTypes = {
        store: storeShape
      }
      return Connect;
    }
  }

猜你喜欢

转载自blog.csdn.net/weixin_42420703/article/details/82227734