Redux 源码剖析

Redux

Redux 是为了处理应用复杂状态流而设计的状态管理库,它吸收了 Flux 架构和函数式编程的优秀思想,提出了应用分层设计的解决方法;

Redux 基本架构

Redux 的将应用分为 Actions、State 和 View 三层;

Actions

Actions 描述用户操作的基本信息,包括操作类型和所需传递的数据;

在代码层面看,一个 action 就是一个对象,实际编码过程中会将 action 设计为 action creator,里面直接封装 action.type,只需要传递数据。

// addTodoAction
export var addToDo = payload => ({
    type: 'ADD_TODO',
    payload,
});

Reducers

Reducer 是根据 action 类型生成新的 state 的函数。这里要求 state 是个 Immutable 对象,因为为了降低性能开销,新旧 state 将采用浅比较,使用 Immutable 对象可以很好匹配这一适用场景。

// todosReducer
var todos = (state = [], action) => {
    switch(action.type) {
        case 'ADD_TODO':
            return [...state, {id: action.id, payload: action.payload}];
        default:
            return state;
    }
}

export default todos;

Store

Store 是存储整个 state 树的仓库,实际上就是一个对象,里面部署了 dispatch() 和 getState() 等主要方法。

import { createStore, combineReducers } from 'redux';
import todosReducer from 'reducers/todosReducer';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';

var rootReducer = combineReducers({
    todos: todosReducer,
});

var store = createStore(rootReducer);

render(
  
    
  ,
  document.getElementById('root')
)

View

View 就是视图层,可以对用户交互给予反馈;

import React from 'react';
import {connect} from 'react-redux';
import * as TodoAction from 'actions/todoAction';

var AddToDo = props => {
    const {
        todos,
        dispatch,
    } = props;

    return 
{ todos.map(todo =>

{todo.name}

) }
; } var mapStateToProps = state => ({ todos: state.todos, }); export default connect(mapStateToProps)(AddToDo);

[图片上传失败...(image-1b3958-1616590677926)]

数据流向

这里以 React 这一 UI 框架为例,讲解一下 React + Redux 的基本数据流向;

  • 首先,UI 从 Store 里面获取 State,这里通过 react-redux 的 Provider 组件实现 store 的注入;
  • UI 发生交互后,会调用 dispatch(action(payload)) 方法,dispatch 方法默认挂载在 store 上;
  • dispatch 触发后,会调用 rootReducer(),rootReducer 会根据之前的 state 和 action 计算新的 state;
  • 新的 state 会重新从根组件传递下去,如果 state 发生变化,则 re-rerender 对应的组件,从而实现视图的更新;

源码解析

源码以 redux 和 react-redux 为内容,为了避免干扰,将会在源码基础上去除本身边界条件、状态锁以及干扰分析部分的代码,并进行简化;

combineReducer

combineReducer 是一个高阶函数, 作用就是将所有的子 reducer 合并为一个根 reducer,当调用 rootReducer 时,内部会遍历所有子 reducer,然后根据每个子 state 是否发生改变,返回新旧的 根 state;

function combineReducer(reducers) {
    var reducerKeys = Object.keys(reducers);
      var finalReducers = {};

      for (var i = 0; i < reducerKeys.length; i++) {
        var key = reducerKeys[i];

        if (typeof reducers[key] === 'function') {
          finalReducers[key] = reducers[key];
        }
      }

      var finalReducerKeys = Object.keys(finalReducers);

    return combine(state, action) {
        // 遍历所有的 reducer,根据前后 state 是否发生变化返回新旧 state

    var hasChanged = false;
    var nextState = {};

    for (var j = 0; j < finalReducerKeys.length; j++) {
      var key = finalReducerKeys[j];
      var reducer = finalReducers[key];
      var prevStateForKey = state[key];
      var nextStateForKey = reducer(prevStateForKey, action);
      nextState[key] = nextStateForKey;
      hasChanged = hasChanged || nextStateForKey !== prevStateForKey;
    }

    hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;

    return hasChanged ? newState : state;
    }
}

createStore

createStore 主要是封装 state、dispatch 和 subscribe 等方法的仓库,提供 UI 组件数据和发射特定类型 action;

function createStore(reducer, prelaodedState, enhancer) {
    var isDispatching = false;
  var currentReducer = reducer;
  var currentState = preloadedState;
  var currentListeners = [];
  var nextListeners = currentListeners;

  function getState() {
    return currentState;
  }

  function dispatch(action) {
    try {
      isDispatching = true;
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    var listeners = (currentListeners = nextListeners);

    for (var i = 0; i < listeners.length; i++) {
      var listener = listeners[i];
      listener();
    }

    return action;
  }

    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);
      currentListeners = null;
    }
  }

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

    // 先调用 dispatch,初始化 state
  dispatch({ type: `@@redux/INIT${/* #__PURE__ */ randomString()}` });

    const store = ({
    dispatch,
    subscribe,
    getState,
  });
    return store;
}

Provider

react-redux 的 Provider 组件采用 React 的 Context 数据传递机制,通过 context 对象将 store 和 state 绑定到各个组件上;

这里在源码的 Provider 组件在实现上进行一定的简化,分离出核心代码:

function Provider({ store, context, children }) {
  var Context = Context || React.createContext(null);
  var contextValue = useMemo(() => {
    return {
      store,
    };
  }, [store]);

  return 
    {children}
  
}

connect

react-redux 的 connect 组件是一个高阶组件,内部通过 useContext 去消费 Provider 提供的 context,将 context.store 和 context.store.getState() 以 props 的方式传递给 connect 的组件,并监听 context.store 的变化;

function createConnect({
  connectHOC = connectAdvanced,
  selectorFactory,
}) {
  return function connect({
    mapStateToProps,
    mapDispatchToProps,
    mergeProps,
  }) {
    return connectHOC(selectorFactory, {
      mapStateToProps,
      mapDispatchToProps,
    });
  }
}

function connectAdvanced(selectorFactory, {
  context = React.createContext(null),
  ...connectOptions,
}) {
  // 这里 context 是从 parent Provider 给的
  var Context = context;

  return function wrapWithConnect(WrappedComponent) {

    function Connect(props) {
      var contextValue = useContext(Context);
      var store = props.store ? props.store : contextValue.store;
      // 实际的框架,通过 mapStateToProps 将根 state 的特定子 state 合并到 props
      var state = store.getState();
      return 
    }

    return Connect;
  }
}

你可能感兴趣的:(Redux 源码剖析)