likes
comments
collection
share

redux核心代码初步实现

作者站长头像
站长
· 阅读数 18
/**
 * createStore(reducer,preloadedState预存储状态,enhancer对store功能进行增强)
 * {
 *      getState,dispatch,subscribe
 * }
 */

function createStore(reducer, preloadedState, enhancer) {
  // 9,约束reducer参数类型
  if (typeof reducer !== "function")
    throw new Error("reducer must be a function");

  // 12判断enchancer是否传递,是否是一个函数
  if (typeof enhancer !== "undefined") {
    if (typeof enhancer !== "function") {
      throw new Error("enhancer must be a function");
    }
    // redux中调用,传入createStore 并返回一个函数,返回reducer, preloadedState
    return enhancer(createStore)(reducer, preloadedState);
  }

  // 1,sotre对象中存储的状态
  var currentState = preloadedState;
  // 6,存放订阅者函数
  var currentListeners = [];
  //   2,获取状态
  function getState() {
    return currentState;
  }
  // 3用于触发action的方法
  function dispatch(action) {
    // 10,判断action是否是一个对象
    if (!isPlainObject(action)) throw new Error("action必须是一个对象");
    // 11,判断action中的type属性是否存在
    if (typeof action.type === "undefined")
      throw new Error("action对象中必须有type属性");
    currentState = reducer(currentState, action); // 根据当前状态和action处理返回新的状态
    // 7循环数组调用订阅者
    for (let i = 0; i < currentListeners.length; i++) {
      // 获取订阅者
      var listener = currentListeners[i];
      // 调用订阅者
      listener();
    }
  }

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

  //   8 返回
  return {
    getState,
    dispatch,
    subscribe,
  };
}

// 4
// store.subscribe(() => {});

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject(obj) {
  // 排除基本数据类型和null
  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; // 返回true就是对象
}

function applyMiddleware(...middlewares) {
  return function (createStore) {
    return function (reducer, preloadedState) {
      // 创建store,拿到store,给中间件传参
      var store = createStore(reducer, preloadedState);
      // 阉割版的 store
      var middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch,
      };
      // 调用中间件的第一层函数 传递阉割版的store对象,返回中间件函数内的里面两层函数
      var chain = middlewares.map((middleware) => middleware(middlewareAPI));
      //  中间件第二层参数传参,第三层就是dispath
      var dispatch = compose(...chain)(store.dispatch);
      // 返回一个增强版的store
      return {
        ...store,
        dispatch,
      };
    };
  };
}

function compose() {
  var funcs = [...arguments];
  console.log(funcs);
  // 因为函数嵌套问题,执行顺序虽然是logger再thunk,为了保证顺序,要倒叙数组,先执行thunk第二层返回dispath,
  return function (dispatch) {
    for (var i = funcs.length - 1; i >= 0; i--) {
      // 第一轮执行返回值返回是thunk里面的函数,他是logger需要的
      dispatch = funcs[i](dispatch);
    }
    return dispatch;
  };
}

// bindActionCreators函数  将action creator函数转换为能够触发action的函数
function bindActionCreators(actionCreators, dispatch) {
  // 创建一个对象用于返回,返回值是一个对象
  // function increment(){
  //   dispatch({type:'increment'})
  // }
  var boundActionCreators = {};
  for (var key in actionCreators) {
    // IIFE解决key变量不能保存的问题
    (function (key) {
      boundActionCreators[key] = function () {
        dispatch(actionCreators[key]());
      };
    })(key);
    // actionCreators[key]()  拿到increment函数,执行,返回action对象
    // dispatch(actionCreators[key]())  dispatch action对象
  }
  return boundActionCreators;
}

// combineReducers组合小的reducer成为大的reducer,返回一个reducer函数
function combineReducers(reducers) {
  //  检查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必须是函数");
  }

  // 调用一个一个的小的reducer 将每一个小的reducer中返回的状态存储在一个新的大的对象中
  return function (state, action) {
    var nextState = {}; // 存储最新的状态
    // 循环reducer拿到最新的状态
    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);
    }
    console.log(nextState)
    return nextState;
  };
}

测试代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <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 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 counterReducer(state, action) {
            switch (action.type) {
                case 'increment':
                    return state + 1;
                case 'decrement':
                    return state - 1
                default:
                    return state
            }
        }

        var rootReducer = combineReducers({
            counter: counterReducer
        })

        // 1创建store
        // const store = createStore(reducer, 0, enhancer)
        const store = createStore(rootReducer, {
            counter: 100
        }, applyMiddleware(logger, thunk))
        console.log(store)

        store.subscribe(function () {
            // 获取最新的状态
            console.log(store.getState())
            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 () {
            // 触发action
            // store.dispatch(function (dispatch) {
            //     setTimeout(function () {
            //         dispatch({
            //             type: 'increment'
            //         })
            //     }, 1000);
            // })

            //   代码执行顺序  logger->thunk->reducer
            // store.dispatch({
            //     type: 'increment'
            // })
            actions.increment()
        }
        document.getElementById('decrement').onclick = function () {
            // store.dispatch({
            //     type: 'decrement'
            // })
            actions.decrement()
        }
    </script>
</body>

</html>