Redux核心源码解析

核心逻辑

目标:自己实现redux的核心逻辑

  • 最核心的就是createStore方法,接收reducer,preloadedState预存储的state状态,enhancer对store的功能进行增加
  • 调用之后会返回一个对象,含有getState获取状态,dispatch,subscribe

源码地址:https://github.com/qifutian/learngit/tree/main/redux%E5%8F%8A%E4%B8%AD%E9%97%B4%E4%BB%B6/myRedux
自定义redux,myRedux.js

/**
 * 
 * createStore(reducer,preloadedState,enhancer)
 * 
 * {getState,dispatch,subscribe}
 */

 function createStore(reducer,preloadedState,enhancer){
     
     // store对象中存储的状态
     var currentState = preloadedState;
     // 存放订阅者函数
     var currentListeners = [];
     // 获取状态 将state放到内存中
     function getState(){
     
         return  currentState
     }

     // 触发action
     function dispatch(action){
     
         currentState = reducer(currentState,action);
         // 循环数组 调用订阅者
         for(var i=0; i<currentListeners.length;i++){
     
             // 获取订阅者
             var listener = currentListeners[i];
             // 调用订阅者
             listener();
         }
     }

     // 订阅状态
     function subscribe(listener) {
     
        currentListeners.push(listener)
     }

     return {
     
         getState,
         dispatch,
         subscribe
     }
 }


//  store.subscribe(() =>{
     

//  })
 

检查是否可用,测试代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="increment">+1</button>
    <span id="box">0</span>
    <button id="decrement">-1</button>
    <script src="./myRedux.js"></script>
   
    
<script>
  function reducer(state,action){
     
     switch(action.type){
     
       case 'increment':
         return state + 1;
        case 'decrement':
          return state - 1;
        default:
          return state;   
     }     
  }

  // 创建store
  var store = createStore(reducer,0);
  console.log(store.getState())

  store.subscribe(function () {
     
      document.getElementById("box").innerHTML = store.getState().counter;
  });

  document.getElementById("increment").onclick = function () {
     

        store.dispatch({
      type: "increment" });
      };

  document.getElementById("decrement").onclick = function () {
     
    store.dispatch({
      type: "decrement" });
  };
</script>
  </body>
</html>

参数类型约束

  • reducer必须是函数
  • action必须是对象
/**
 * 
 * createStore(reducer,preloadedState,enhancer)
 * 
 * {getState,dispatch,subscribe}
 */

 function createStore(reducer,preloadedState,enhancer){
     
    // 约束reducer参数类型
    if(typeof reducer!== 'function') throw new Error("reducer必须是函数")
    
     // store对象中存储的状态
     var currentState = preloadedState;
     // 存放订阅者函数
     var currentListeners = [];
     // 获取状态 将state放到内存中
     function getState(){
     
         return  currentState
     }

     // 触发action
     function dispatch(action){
     
 // 判断action是否是一个对象
    if (!isPlainObject(action)) throw new Error('action必须是一个对象');
    // 判断action中的type属性是否存在
    if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
         currentState = reducer(currentState,action);
         // 循环数组 调用订阅者
         for(var i=0; i<currentListeners.length;i++){
     
             // 获取订阅者
             var listener = currentListeners[i];
             // 调用订阅者
             listener();
         }
     }

     // 订阅状态
     function subscribe(listener) {
     
        currentListeners.push(listener)
     }

     return {
     
         getState,
         dispatch,
         subscribe
     }
 }


//  store.subscribe(() =>{
     

//  })
 

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
     
    if (typeof obj !== 'object' || obj === null) return false;
    var proto = obj;
    while (Object.getPrototypeOf(proto) != null) {
     
      proto = Object.getPrototypeOf(proto)
    }
    return Object.getPrototypeOf(obj) === proto;
  }

实现Enhancer方法

通过Enhancer方法对返回的store进行功能上的增强

redux的中间件就是对Enhancer方法的应用,执行异步代码

/**
 * 
 * createStore(reducer,preloadedState,enhancer)
 * 
 * {getState,dispatch,subscribe}
 */

 function createStore(reducer,preloadedState,enhancer){
     
    // 约束reducer参数类型
    if(typeof reducer!== 'function') throw new Error("reducer必须是函数")
    // 判断对象中是否含有type属性
    if (typeof enhancer !== 'undefined') {
     
        if (typeof enhancer !== 'function') {
     
          throw new Error('enhancer必须是函数')
        }
        return enhancer(createStore)(reducer, preloadedState);
    }
     // store对象中存储的状态
     var currentState = preloadedState;
     // 存放订阅者函数
     var currentListeners = [];
     // 获取状态 将state放到内存中
     function getState(){
     
         return  currentState
     }

     // 触发action
     function dispatch(action){
     
        // 判断action是否是一个对象
        if (!isPlainObject(action)) throw new Error('action必须是一个对象');
        // 判断action中的type属性是否存在
        if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
         currentState = reducer(currentState,action);
         // 循环数组 调用订阅者
         for(var i=0; i<currentListeners.length;i++){
     
             // 获取订阅者
             var listener = currentListeners[i];
             // 调用订阅者
             listener();
         }
     }

     // 订阅状态
     function subscribe(listener) {
     
        currentListeners.push(listener)
     }

     return {
     
         getState,
         dispatch,
         subscribe
     }
 }


//  store.subscribe(() =>{
     

//  })
 

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
     
    if (typeof obj !== 'object' || obj === null) return false;
    var proto = obj;
    while (Object.getPrototypeOf(proto) != null) {
     
      proto = Object.getPrototypeOf(proto)
    }
    return Object.getPrototypeOf(obj) === proto;
  }

html


<script>

  function enhancer(createStore){
     
     return function (reducer,preloadedState){
     
        var store = createStore(reducer,preloadedState)
        var dispatch = store.dispatch;
        function _dispatch (action) {
     
            if (typeof action === 'function') {
     
              return action(dispatch)
            }
            dispatch(action);
        }
        return {
     
          ...store,
          dispatch: _dispatch
        }
     }
  }

  function reducer(state,action){
     
     switch(action.type){
     
       case 'increment':
         return state + 1;
        case 'decrement':
          return state - 1;
        default:
          return state;   
     }     
  }

  // 创建store
  var store = createStore(reducer,0);
  console.log(store.getState())

  store.subscribe(function () {
     
      document.getElementById("box").innerHTML = store.getState().counter;
  });

  document.getElementById("increment").onclick = function () {
     

        // store.dispatch({ type: "increment" });
        store.dispatch(function(dispatch){
     
          setTimeout(function(){
     
            dispatch({
     type: 'increment'})
          }, 1000)
        })
  };

  document.getElementById("decrement").onclick = function () {
     
    store.dispatch({
      type: "decrement" });
  };
</script>

redux实现applyMiddleware

  • 正常的流程,action会被reducer接收到,
  • 但是加入到中间件后,action会被中间件接收到,当一个个中间件执行完之后,action才会被传递给reducer
  • action就是在被中间件发出之后,在reducer接收到之前做些操作
  • applyMiddleWare方法就是对dispatch函数的增强,通过增强dispatch,让多个中间件进行组合,达到多个中间件安装顺序执行

创建中间件
logger.js

store是仓库,next是下一个中间件,就是源码里的reducer
function logger (store) {
     
  return function (next) {
     
    return function (action) {
     
      console.log('logger');
      next(action)
    }
  }
}

实现applyMiddleware方法

  • 在applyMiddleware方法中需要return一个函数作为参数的调用是createStore的样式,在return函数接收reducer和preloadedState
  • 先创建store,在传入reducer和preloadedState
  • 在调用中间件的第一次函数,传递store对象
  • 声明compose方法,进行循环调用中间件,调用的就是最里层的函数
 
function applyMiddleware (...middlewares) {
     
    return function (createStore) {
     
      return function (reducer, preloadedState) {
     
        // 创建 store
        var store = createStore(reducer, preloadedState);
        // 阉割版的 store
        var middlewareAPI = {
     
          getState: store.getState,
          dispatch: store.dispatch
        }
        // 调用中间件的第一层函数 传递阉割版的store对象
        var chain = middlewares.map(middleware => middleware(middlewareAPI));
        var dispatch = compose(...chain)(store.dispatch);
        return {
     
          ...store,
          dispatch
        }
      }
    }
}

function compose () {
     
    var funcs = [...arguments];
    return function (dispatch) {
     
      for (var i = funcs.length - 1; i >= 0; i--) {
     
        dispatch = funcs[i](dispatch);
      }
      return dispatch;
    }
  }

redux中的bindActionCreators

一、将参数对象转换为同名 key 对象
二、应用场景

如果参数 actionCreators 类型为 function,则直接执行这个函数。 如果参数 actionCreators 类型不是非
null 的对象,抛错; 如果参数 actionCreators 类型为非 null 对象,则浅复制一份给变量
boundActionCreators。返回值为 boundActionCreators 。其目的是将参数对象转换成一个同名 key 对象。

当你需要将 actions 传给子组件,但是又不想子组件有任何 Redux 的存在,或者不行层层传递 dispatch 时 。可以使用
bindActionCreators。

bindActionCreators是redux的一个自带函数,作用是将单个或多个ActionCreator转化为dispatch(action)的函数集合形式。

开发者不用再手动dispatch(actionCreator(type)),而是可以直接调用方法。

可以实现简化书写,减轻开发的负担。

例如:

return {
     
        // 当触发addNews时,dispatch会执行异步action
        addNews(){
     
            dispatch(async (a)=>{
     
                // console.log(a == dispatch);// false dispatch
                await axios.post("http://127.0.0.1/news",{
     
                    newsTitle:this.refs.newsTitle.value,
                    newsHref:this.refs.newsHref.value
                });
                this.props.getNews();
            })
        },
        getNews(){
     
            dispatch(async (b)=>{
     
                console.log(b)
                // 获取新闻列表,并将新闻列表放到仓库状态中。
                const {
     data} = await axios.get("http://127.0.0.1/news");
                // dispatch(changeNewsList(data.newsList))
                dispatch(changeNewsList(data.newsList))
            })
        }
    }
利用bindActionCreators:
 return {
     
        newsList:state.news.newsList
    }
}
// 操作状态。
function mapDispatchToProps(dispatch) {
     

    return bindActionCreators(newsCreatore,dispatch);
}

通过actions对象调用方法,就可以dispatch所有的action

源码实现

function bindActionCreators (actionCreators, dispatch) {
     
  var boundActionCreators = {
     };
  for (var key in actionCreators) {
     
    (function (key) {
     
      boundActionCreators[key] = function () {
     
        dispatch(actionCreators[key]())
      }
    })(key)
  }
  return boundActionCreators;
}

redux的combineReducers实现

随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分。

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。

合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。

最终,state 对象的结构会是这样的:

{
     
  reducer1: ...
  reducer2: ...
}

通过为传入对象的 reducer 命名不同来控制 state key 的命名。例如,你可以调用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 将 state 结构变为 { todos, counter }。

通常的做法是命名 reducer,然后 state 再去分割那些信息,因此你可以使用 ES6 的简写方法:combineReducers({ counter, todos })。这与 combineReducers({ counter: counter, todos: todos }) 一样。

源码实现


function combineReducers (reducers) {
     
  // 1. 检查reducer类型 它必须是函数
  var reducerKeys = Object.keys(reducers);
  for (var i = 0; i < reducerKeys.length; i++) {
     
    var key = reducerKeys[i];
    if (typeof reducers[key] !== 'function') throw new Error('reducer必须是函数');
  }
  // 2. 调用一个一个的小的reducer 将每一个小的reducer中返回的状态存储在一个新的大的对象中
  return function (state, action) {
     
    var nextState = {
     };
    for (var i = 0; i < reducerKeys.length; i++) {
     
      var key = reducerKeys[i];
      var reducer = reducers[key];
      var previousStateForKey = state[key];
      nextState[key] = reducer(previousStateForKey, action)
    }
    return nextState;
  }
}

整体代码

function createStore (reducer, preloadedState, enhancer) {
     
  // reducer 类型判断 
  if (typeof reducer !== 'function') throw new Error('redcuer必须是函数');

  if (typeof enhancer !== 'undefined') {
     
    if (typeof enhancer !== 'function') {
     
      throw new Error('enhancer必须是函数')
    }
    return enhancer(createStore)(reducer, preloadedState);
  }
  // 状态
  var currentState = preloadedState;
  // 订阅者
  var currentListeners = [];
  // 获取状态
  function getState () {
     
    return currentState;
  }
  // 用于触发action的方法
  function dispatch (action) {
     
    // 判断action是否是一个对象
    if (!isPlainObject(action)) throw new Error('action必须是一个对象');
    // 判断action中的type属性是否存在
    if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
    // 调用reducer函数 处理状态
    currentState = reducer(currentState, action);
    // 调用订阅者 通知订阅者状态发生了改变
    for (var i = 0; i < currentListeners.length; i++) {
     
      var listener = currentListeners[i];
      listener();
    }
  }
  // 订阅状态的改变
  function subscribe (listener) {
     
    currentListeners.push(listener);
  }

  // 默认调用一次dispatch方法 存储初始状态(通过reducer函数传递的默认状态)
  dispatch({
     type: 'initAction'})

  return {
     
    getState,
    dispatch,
    subscribe
  }
}

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
     
  if (typeof obj !== 'object' || obj === null) return false;
  var proto = obj;
  while (Object.getPrototypeOf(proto) != null) {
     
    proto = Object.getPrototypeOf(proto)
  }
  return Object.getPrototypeOf(obj) === proto;
}

function applyMiddleware (...middlewares) {
     
  return function (createStore) {
     
    return function (reducer, preloadedState) {
     
      // 创建 store
      var store = createStore(reducer, preloadedState);
      // 阉割版的 store
      var middlewareAPI = {
     
        getState: store.getState,
        dispatch: store.dispatch
      }
      // 调用中间件的第一层函数 传递阉割版的store对象
      var chain = middlewares.map(middleware => middleware(middlewareAPI));
      var dispatch = compose(...chain)(store.dispatch);
      return {
     
        ...store,
        dispatch
      }
    }
  }
}

function compose () {
     
  var funcs = [...arguments];
  return function (dispatch) {
     
    for (var i = funcs.length - 1; i >= 0; i--) {
     
      dispatch = funcs[i](dispatch);
    }
    return dispatch;
  }
}

function bindActionCreators (actionCreators, dispatch) {
     
  var boundActionCreators = {
     };
  for (var key in actionCreators) {
     
    (function (key) {
     
      boundActionCreators[key] = function () {
     
        dispatch(actionCreators[key]())
      }
    })(key)
  }
  return boundActionCreators;
}

function combineReducers (reducers) {
     
  // 1. 检查reducer类型 它必须是函数
  var reducerKeys = Object.keys(reducers);
  for (var i = 0; i < reducerKeys.length; i++) {
     
    var key = reducerKeys[i];
    if (typeof reducers[key] !== 'function') throw new Error('reducer必须是函数');
  }
  // 2. 调用一个一个的小的reducer 将每一个小的reducer中返回的状态存储在一个新的大的对象中
  return function (state, action) {
     
    var nextState = {
     };
    for (var i = 0; i < reducerKeys.length; i++) {
     
      var key = reducerKeys[i];
      var reducer = reducers[key];
      var previousStateForKey = state[key];
      nextState[key] = reducer(previousStateForKey, action)
    }
    return nextState;
  }
}

测试自定义的html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="increment">+1</button>
    <span id="box">0</span>
    <button id="decrement">-1</button>
    <script src="./myRedux.js"></script>
    <script src="middlewares/logger.js"></script>
    <script src="middlewares/thunk.js"></script>
    <script>
      function counterReducer(state, action) {
     
        switch (action.type) {
     
          case "increment":
            return state + 1;
          case "decrement":
            return state - 1;
          default:
            return state;
        }
      }

      function enhancer (createStore) {
     
        return function (reducer, preloadedState) {
     
          var store = createStore(reducer, preloadedState);
          var dispatch = store.dispatch;
          function _dispatch (action) {
     
            if (typeof action === 'function') {
     
              return action(dispatch)
            }
            dispatch(action);
          }
          return {
     
            ...store,
            dispatch: _dispatch
          }
        }
      }
      
      var rootReducer = combineReducers({
     counter: counterReducer})
      
      var store = createStore(rootReducer, {
     counter: 100}, applyMiddleware(logger, thunk));

      store.subscribe(function () {
     
        document.getElementById("box").innerHTML = store.getState().counter;
      });

      var actions = bindActionCreators({
     increment, decrement}, store.dispatch);

      function increment () {
     
        return {
     type: "increment"}
      }

      function decrement () {
     
        return {
     type: "decrement"};
      }
      
      document.getElementById("increment").onclick = function () {
     
        // logger -> thunk -> reducer
        // store.dispatch({ type: "increment" });
        actions.increment()
      };

      document.getElementById("decrement").onclick = function () {
     
        // store.dispatch({ type: "decrement" });
        actions.decrement()
      };

    </script>
  </body>
</html>

你可能感兴趣的:(react,react源码解析,redux,reactjs)