Redux原理简析与源码解析

本文发布在个人博客javascript

redux版本是最新的v4.0.5java

原理简析

先简单叙述一下部分的实现原理react

createStore

createStore(reducer, preloadedState, enhancer)
复制代码

调用createStore有2种状况,传入了加强器enhancer时,会将createStore传入enhancer,也就是在enhancer里建立了store后,把store的方法加强后再返回,代码来看就是git

enhancer(createStore)(reducer, preloadedState)
复制代码

另外一种就是忽略掉了enhancer,直接建立storegithub

在建立完store后,内部会dispatch一个type: ActionTypes.INITaction,由于咱们的reducer不会有这个type的,因此会返回初始值,这一步就是给整个state tree赋初始值了redux

一般,咱们都不会只有一个reducer,因此须要使用redux提供的合并reducer的函数combineReducers数组

combineReducers

combineReducers的原理就是依次调用传入对象的值的reducer函数闭包

combineReducers(reducers)
复制代码

简单来理解伪代码能够是这样app

// 传入
const reducers = {
  count: state => state,
  string: state => state
};

// 函数里处理

const keys = ["count", "string"];
// 新state
const state = {};
for (const key of keys) {
  // 经过上一次key对应的state,调用对应的reducer函数,获得新的state
  state[key]=reducers[key](prevKeyState)
}
return state;
复制代码

applyMiddleware

applyMiddlewareredux自带的加强器,主要是用来加强dispatch函数的功能,也就是提供了dispatch函数的中间件函数

以前有讲,若是传入了enhancer,会将createStore交给加强器来办,好比使用的applyMiddleware,流程大概就是这样

// createStore将本身交给了加强器
applyMiddleware(加强器A,加强器B)(createStore)(reducers,preloadedState)

// 函数声明大概就是这样
function applyMiddleware(加强器A,加强器B) {
  return function (createStore) {
    return function (reducers,preloadedState) {
        const state = createStore(reducers,preloadedState);
        // 取出dispatch 使用接收的加强器对他进行加强
        ...
    }
  }
}
复制代码

接下来就直接铺上源码

源码解析

如下tsc转出的JavaScript版,而且我人为的省略掉了一些类型判断和抛出错误

本文详细代码见个人github仓库

createStore

import $$observable from "./utils/symbol-observable";
import ActionTypes from "./utils/actionTypes";

export default function createStore(reducer, preloadedState, enhancer) {  
  	// 若是有加强器
  	if (typeof enhancer !== "undefined"){
    	return enhancer(createStore)(reducer, preloadedState);
    }

    // 当前的reducer
    let currentReducer = reducer;
    // 当前的state
    let currentState = preloadedState;
    // 当前的listeners
    let currentListeners = [];
    // 下一次的listeners
    let nextListeners = currentListeners;
    // 标示是否正在进行dispatch
    let isDispatching = false;

     // 这是一个对currentListeners的浅复制,因此咱们能够将nextListeners看成一个临时的list在dispatch的过程当中使用
     // 这样作的目的是能够防止在dispatch调用过程当中,调用subscribe/unsubscribe产生错误
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
            // 浅复制
            nextListeners = currentListeners.slice();
        }
    }


    // 用来获取state tree
    function getState() {
        return currentState;
    }
    
  	// 添加一个state change的监听,它会在每次dispatch调用结束后而且一部分state tree可能被改变时调用
  	// 你能够在这个callback里调用getState()来获取当前的state tree
  	// 返回值是一个函数,用来退订
    function subscribe(listener) {
        // 标志已经被订阅
        let isSubscribed = true;
        // 浅复制一次listeners
        // 也就是currentListeners复制到nextListeners
        ensureCanMutateNextListeners();
        // 添加进nextListeners
        nextListeners.push(listener);
        // 返回的退订函数
        return function unsubscribe() {
            // 若是已经退订了,就return
            // 防止屡次调用函数
            if (!isSubscribed) {
                return;
            }
            // 已经退订
            isSubscribed = false;
            // 浅复制一次
            ensureCanMutateNextListeners();
            const index = nextListeners.indexOf(listener);
            // 删除掉订阅的函数
            nextListeners.splice(index, 1);
            // currentListeners设置为null的缘由是防止内存泄露
            // 见https://github.com/reduxjs/redux/issues/3474
            currentListeners = null;
        };
    }

     // dispatch一个action,这是触发state改变的惟一方式
     // 它只实现了基础的字面量对象action操做,若是你想要dispatch一个Promise、Observable、thunk获取其余的,你须要将建立store的函数放进响应的中间件,好比redux-thunk包
     // 为了方便返回值为相同的action对象
     // 若是你使用来自定义的中间件,可能会返回其余的东西,好比Promise
    function dispatch(action) {
        try {
            isDispatching = true;
            // 经过reducer获取下一个state
            currentState = currentReducer(currentState, action);
        }
        finally {
            isDispatching = false;
        }
        // 通知全部listeners
        const listeners = (currentListeners = nextListeners);
        for (let i = 0; i < listeners.length; i++) {
            const listener = listeners[i];
            listener();
        }
        return action;
    }
    
     // 替换store当前使用的reducer来计算state
     // 若是你的app实现了代码分割,而且你想动态的加载某些reducers,或者实现来redux的热重载,就须要这个方法
    function replaceReducer(nextReducer) {
      // ...
    }
    
    // 提供给observable/reactive库的接口
    function observable() {
      // ...
    }

    // 当store建立好了,会派发一个INIT的action,这样全部的reducer都会返回它们的初始值
    // 有效填充了初始的state tree
    dispatch({ type: ActionTypes.INIT });
    const store = {
        dispatch: dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
    };
    // 返回store
    return store;
}

复制代码

combineReducers

combineReducers的做用就是将多个reducer合并为一个

import ActionTypes from "./utils/actionTypes";

export default function combineReducers(reducers) {
    // 获取传入的reducers对象的keys
    const reducerKeys = Object.keys(reducers);
    // 实际使用的reducers对象
    const finalReducers = {};
    for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i];
        finalReducers[key] = reducers[key];
    }
    // 获取reducers的key
    const finalReducerKeys = Object.keys(finalReducers);
    // 返回的合并为一个的reducer函数
    return function combination(state = {}, action) {
        // 标示state有没有改变
        let hasChanged = false;
        // 通过reducer处理的下一次state
        const nextState = {};
        // 循环调用每个reducer函数
        for (let i = 0; i < finalReducerKeys.length; i++) {
            // 当前reducer的key
            const key = finalReducerKeys[i];
            // 当前reducer的函数
            const reducer = finalReducers[key];
            // 当前key对应的state的值
            const previousStateForKey = state[key];
            // 通过reducer函数后的下一此state值
            const nextStateForKey = reducer(previousStateForKey, action);
            // 当前key的值赋值给state对象
            nextState[key] = nextStateForKey;
            // 若是当前key的state和上一次的state不一样,说明state就已经改变了
            hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
        }
        // 若是replace了reducers,可能会须要判断key的length
        // 见https://github.com/reduxjs/redux/issues/3488
        hasChanged =
            hasChanged || finalReducerKeys.length !== Object.keys(state).length;
        return hasChanged ? nextState : state;
    };
}
复制代码

bindActionCreators

bindActionCreators的做用是简化操做,能够把dispatch包装进咱们的action creator函数

// 绑定
function bindActionCreator(actionCreator, dispatch) {
    return function (...args) {
        return dispatch(actionCreator.apply(this, args));
    };
}
export default function bindActionCreators(actionCreators, dispatch) {
    // 只有一个函数的状况
    if (typeof actionCreators === "function") {
        return bindActionCreator(actionCreators, dispatch);
    }
    const boundActionCreators = {};
    // 循环绑定
    for (const key in actionCreators) {
        const actionCreator = actionCreators[key];
        boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
    return boundActionCreators;
}

复制代码

compose

compose函数是中间件applyMiddleware的核心功能,能将多个单参数函数从右到左嵌套调用

它的调用形式以下

const a = a => a + "A";
const b = b => b + "B";
const c = c => c + "C";

const composed = compose(a,b,c);

composed("args"); // => argsCBA
复制代码

源码以下

export default function compose(...funcs) {
  	// 参数为0个
    if (funcs.length === 0) {
        return (arg) => arg;
    }
  	// 参数为1个
    if (funcs.length === 1) {
        return funcs[0];
    }
    return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

复制代码

applyMiddleware

applyMiddlewareredux自带的加强器,用来加强dispatch功能

import compose from "./compose";
/** * applyMiddleware函数接收middleware为参数 * 返回另外一个函数,这个函数须要接收createStore为函数,这个处理是在createStore中进行的 * * 这里是使用接收的createStore函数,把store建立出来 * 而后把dispatch和getStore传给中间件函数 * 使用compose把已经有dispatch和getStore方法当中间件组合后,将dispatch传入,获得一个新的dispatch * 新的dispatch是通过了中间件的dispatch */
export default function applyMiddleware(...middlewares) {
    return (createStore) => (reducer, ...args) => {
        const store = createStore(reducer, ...args);
        // 这里作一个错误处理
        // 若是在绑定中间件的时候调用dispatch会报错
        let dispatch = () => {
            throw new Error("...");
        };
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (action, ...args) => dispatch(action, ...args)
        };
        // 将dispatch和getStore方法传入中间件,获得新的数组
        const chain = middlewares.map(middleware => middleware(middlewareAPI));
        // 将新的数组用compose绑定起来,再把store.dispatch传入,获得新的dispatch
        dispatch = compose(...chain)(store.dispatch);
        return {
            ...store,
            dispatch
        };
    };
}
复制代码

阅读完源码后,我最大的感慨仍是redux的闭包运用,基本到处都是闭包

其中以为最精妙的部分是applyMiddleware,感受好像不少地方都能用上这个概念,因此我也写了一篇详细解析,Redux的中间件applyMiddleware解析

阅读到这里的朋友也许也在阅读源码,最近我有看一些源码解析文章,就在思考,如何才能把源码解析给说清楚,后来想一想,源码源码,想必也都是代码了,靠文字确定是说不清的

最后,祝你们身体健康,工做顺利!

欢迎你们关注个人公众号~

相关文章
相关标签/搜索