关于 Redux 源码的实现及分析

本文将跟随 Crystal 分析并实现 Redux 的几个核心API,包括且不限于 createStoregetStatedispatchsubscribe
最终效果可完成对 couter 的基本增减、异步增减功能的支持

Redux 回顾 Part

在开始手撕 Redux 实现之前我们需要先回顾一下 Redux 的几个核心API

createStore
我们使用 Redux 第一步,需要创建 Store 容器,就需要使用到 createStore 这个 API
它接收三个参数,第一个是 reducer 函数,第二个是默认状态(可选),第三个是enhancer 用来增强redux功能
返回一个状态容器,包含 getStatedispatchsubscribe 等方法

getState
我们可以通过该方法获取 store 中的状态

dispatch
我们通过 dispatch 方法来触发 Action

subscribe
store 中的状态发生改变,会执行 subscribe 方法

Redux 实现 及 分析 Part

通过前面部分的分析我们知道核心API 都是经过 createStore 进行返回的,那么我们第一件事情就是来实现一下这个 createStore

createStore

/* 接收三个参数, 返回**核心API */
function createStore(reducer, preloadedState, enhancer) {
	return {
		getState: () => {},
		dispatch: () => {},
		subscribe: () => {},
	}
}

我们的壳子已经搭建完成,可以添加具体细节了~

getState

getState 方法的作用就是返回 store 中的状态,并且我们 createStore 第二个参数就是初始状态。故事线出来了~

function createStore(reducer, preloadedState, enhancer) {
    // 状态值
    var currentState = preloadedState;
   
    // 获取状态
    function getState () {
        return currentState
    }
    
	return {
		getState
	}
}

subscribe

状态的订阅者可能有多个,也可能会被多次调用,所以需要一个数组进行存储

function createStore(reducer, preloadedState) {
    // 订阅者函数
    var currentListeners = []

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

dispatch

dispatch 的作用是 触发 action; 目的是为了传递给 reducer , 让 reduceraction 进行处理后返回新的 statestore 完成状态更新;
状态更新需要通过 subscribe 来通知订阅者(📢 订阅者可以有多个)

function createStore(reducer, preloadedState, enhancer) {
	...
	    
    // 触发 action
    function dispatch (action) {
        currentState = reducer(currentState, action)
        // 调用订阅者 通知订阅者状态发生了改变
        for (var i = 0; i < currentListeners.length; i++) {
            // 获取订阅者
            var listener = currentListeners[i];
            // 调用订阅者
            listener();
        }
    }
    return {
		dispatch
	}
}

接下来我们加点小细节,来点小约束~

reducer 必须是一个函数

function createStore(reducer, preloadedState) {
    if (typeof reducer !== 'function') throw new Error("reducer must be a function")
  	...
}

action 必须是一个含有 type 属性的对象

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
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;
}

function createStore(reducer, preloadedState) {
	...

    // 触发 action
    function dispatch (action) {
        // 判断action是否是一个对象
        if (!isPlainObject(action)) throw new Error('action must be a object');
        // 判断action中的type属性是否存在
        if (typeof action.type === 'undefined') throw new Error('There must be a type attribute in the action object');
				...
    }
}

enhancer

通过 enhancer 可以让 createStore 的调用者对返回的 store 对象进行功能上的增强

规定 enhancer 必须是一个函数;
在调用 enhancer 函数的时候,将 createStore 方法本身、preloadedStatereducer 都传递出去

function createStore(reducer, preloadedState, enhancer) {
    // 判断是否传递了 enhancer 函数 && enhancer 是一个函数
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
          throw new Error('enhancer must be a function')
        }
        // 这里的返回想想我们写中间件的时候昂~
        return enhancer(createStore)(reducer, preloadedState);
    }
}

中途小憩
目前我们已经基本功能完成了,可以来一波测试验证,把demo代码发来瞅瞅哈

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>redux 实现部分测试 demo</title>
</head>
<body>
    <button id="increment">+1</button>
    <span id="box">0</span>
    <button id="decrement">-1</button>

    <script src="../redux/index.js"></script>
    <script>
        function reducer(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;
                // 对 dispatch 的增强:模拟实现 redux-thunk
                function _dispatch (action) {
                    if (typeof action === 'function') {
                        return action(dispatch)
                    }
                    dispatch(action);
                }
                return {
                    ...store,
                    dispatch: _dispatch
                }
            }
        }

        const store = createStore(reducer, 0, enhancer);
        
        // 获取最新状态
        store.subscribe(function () {
            document.getElementById("box").innerHTML = store.getState();
        });
      
      	/* action 及 action 的触发 */
        document.getElementById("increment").onclick = function () {
            store.dispatch({ type: "increment" });
        };

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

小憩ending,我们继续

applyMiddleware

applyMiddleware 的作用是通过增强 dispatch 方法,让多个中间件函数进行组合以达到我们在触发 action 的时候让多个中间件按照顺序进行执行

里面涉及到的 next 参数需要注意一下:指向的是下一个中间件函数(最里层的函数);最后一个中间件的next 指向 dispatch 方法

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
            }
            // 返回 中间件 函数的第二层函数
            var chain = middlewares.map(middleware => middleware(middlewareAPI));
            // 最里层函数   
            var dispatch = compose(...chain)(store.dispatch);
            return {
                ...store,
                dispatch
            }
        }
    }
}

function compose () {
    var funcs = [...arguments];
    // 想要拿到 next 函数,需要倒序执行获取
    return function (dispatch) {
        for (var i = funcs.length - 1; i >= 0; i--) {
            // 执行第二层函数,返回最里层的函数
            dispatch = funcs[i](dispatch);
        }
        // 返回的最里层的函数
        return dispatch;
    }
}

bindActionCreators

bindActionCreators 的作用是将 actionCreator 函数转换成能够触发 action 的函数,它返回一个对象

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

combineReducers

combineReducers 的作用是将小的 reducer 通过该方法组合成一个大的 reducer返回值,返回一个 reducer 函数

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 must be a function');
    }
    // 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;
    }
}

完整代码 Link

完整代码详见 Github

// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
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;
}

function createStore (reducer, preloadedState, enhancer) {
    // reducer 类型判断
    if (typeof reducer !== 'function') throw new Error("reducer must be a function")

    // 判断是否传递了 enhancer 函数 && enhancer 是一个函数
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
          throw new Error('enhancer must be a function')
        }
        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 must be a object');
        // 判断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
    }
}


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
            }
            // 返回 中间件 函数的第二层函数
            var chain = middlewares.map(middleware => middleware(middlewareAPI));
            // 最里层函数   
            var dispatch = compose(...chain)(store.dispatch);
            return {
                ...store,
                dispatch
            }
        }
    }
}

function compose () {
    var funcs = [...arguments];
    // 想要拿到 next 函数,需要倒序执行获取
    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 must be a function');
    }
    // 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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值