Redux原理简析与源码解析
2020/3/20 11:01:28
本文主要是介绍Redux原理简析与源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
本文发布在我的博客
redux
版本是最新的v4.0.5
原理简析
先简单叙述一下部分的实现原理
createStore
createStore(reducer, preloadedState, enhancer) 复制代码
调用createStore
有2种情况,传入了增强器enhancer
时,会将createStore
传入enhancer
,也就是在enhancer
里创建了store
后,把store
的方法增强后再返回,代码来看就是
enhancer(createStore)(reducer, preloadedState) 复制代码
另一种就是忽略掉了enhancer
,直接创建store
在创建完store
后,内部会dispatch
一个type: ActionTypes.INIT
的action
,因为我们的reducer
不会有这个type
的,所以会返回初始值,这一步就是给整个state tree
赋初始值了
通常,我们都不会只有一个reducer
,所以需要使用redux
提供的合并reducer
的函数combineReducers
combineReducers
combineReducers
的原理就是依次调用传入对象的值的reducer
函数
combineReducers(reducers) 复制代码
简单来理解伪代码可以是这样
// 传入 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
applyMiddleware
是redux
自带的增强器,主要是用来增强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
applyMiddleware
是redux
自带的增强器,用来增强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解析
阅读到这里的朋友也许也在阅读源码,最近我有看一些源码解析文章,就在思考,如何才能把源码解析给说清楚,后来想想,源码源码,想必也都是代码了,靠文字肯定是说不清的
最后,祝大家身体健康,工作顺利!
欢迎大家关注我的公众号~
这篇关于Redux原理简析与源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南