共计 8209 个字符,预计需要花费 21 分钟才能阅读完成。
1、本文不涉及 redux 的使用方法,因此可能更适合使用过 redux 的同学阅读 2、当前 redux 版本为 4.0.1
Redux 作为大型 React 应用状态管理最常用的工具。虽然在平时的工作中很多次的用到了它,但是一直没有对其原理进行研究。最近看了一下源码,下面是我自己的一些简单认识,如有疑问欢迎交流。
1.createStore
结合使用场景我们首先来看一下 createStore 方法。
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > // 这是我们平常使用时创建 store </span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> store = <span class="hljs-built_in">createStore</span>(reducers, state, enhance);</td></tr></tbody></table> |
以下源码为去除异常校验后的源码,
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-keyword" > export </span> <span class = "hljs-keyword" > default </span> <span class = "hljs-keyword" > function </span> <span class = "hljs-title function_" >createStore</span>(<span class = "hljs-params" >reducer, preloadedState, enhancer</span>) {</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "2" ><div class = "hljs-ln-n" data-line-number= "2" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "2" ><span class = "hljs-comment" > // 如果有传入合法的 enhance,则通过 enhancer 再调用一次 createStore</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> enhancer !== <span class="hljs-string">'undefined'</span>) {<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> enhancer !== <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Expected the enhancer to be a function.'</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">return</span> <span class="hljs-title function_">enhancer</span>(createStore)(reducer, preloadedState) <span class="hljs-comment">// 这里涉及到中间件,后面介绍 applyMiddleware 时在具体介绍</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">let</span> currentReducer = reducer <span class="hljs-comment">// 把 reducer 赋值给 currentReducer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-keyword">let</span> currentState = preloadedState <span class="hljs-comment">// 把 preloadedState 赋值给 currentState</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">let</span> currentListeners = [] <span class="hljs-comment">// 初始化监听函数列表</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-keyword">let</span> nextListeners = currentListeners <span class="hljs-comment">// 监听列表的一个引用</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"><span class="hljs-keyword">let</span> isDispatching = <span class="hljs-literal">false</span> <span class="hljs-comment">// 是否正在 dispatch</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">function</span> <span class="hljs-title function_">ensureCanMutateNextListeners</span>(<span class="hljs-params"></span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-keyword">function</span> <span class="hljs-title function_">getState</span>(<span class="hljs-params"></span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-keyword">function</span> <span class="hljs-title function_">subscribe</span>(<span class="hljs-params">listener</span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-keyword">function</span> <span class="hljs-title function_">dispatch</span>(<span class="hljs-params">action</span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"><span class="hljs-keyword">function</span> <span class="hljs-title function_">replaceReducer</span>(<span class="hljs-params">nextReducer</span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"><span class="hljs-comment">// 在 creatorStore 内部没有看到此方法的调用,就不讲了</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"><span class="hljs-keyword">function</span> <span class="hljs-title function_">observable</span>(<span class="hljs-params"></span>) {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24"><span class="hljs-comment">// 初始化 store 里的 state tree</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25"><span class="hljs-title function_">dispatch</span>({<span class="hljs-attr">type</span>: <span class="hljs-title class_">ActionTypes</span>.<span class="hljs-property">INIT</span>})</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26"> </td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"><span class="hljs-keyword">return</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">dispatch,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">subscribe,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30">getState,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31">replaceReducer,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32">[$$observable]: observable</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34">}</td></tr></tbody></table> |
我们可以看到 creatorStore 方法除了返回我们常用的方法外,还做了一次初始化过程 dispatch({type: ActionTypes.INIT}); 那么 dispatch 干了什么事情呢?
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" ><span class = "hljs-comment" > /**</span></span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-comment">* dispath action。这是触发 state 变化的惟一途径。* <span class="hljs-doctag">@param</span> {<span class="hljs-type">Object</span>} 一个普通 (plain) 的对象,对象当中必须有 type 属性</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-comment">* <span class="hljs-doctag">@returns</span> {<span class="hljs-type">Object</span>} 返回 dispatch 的 action</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-comment">*/ </span></td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "5" ><div class = "hljs-ln-n" data-line-number= "5" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "5" ><span class = "hljs-keyword" > function </span> <span class = "hljs-title function_" >dispatch</span>(<span class = "hljs-params" >action</span>) {</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "6" ><div class = "hljs-ln-n" data-line-number= "6" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "6" ><span class = "hljs-comment" > // 判断 dispahch 正在运行,Reducer 在处理的时候又要执行 dispatch</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-keyword">if</span> (isDispatching) {<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Reducers may not dispatch actions.'</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">try</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-comment">// 标记 dispatch 正在运行</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">isDispatching = <span class="hljs-literal">true</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-comment">// 执行当前 Reducer 函数返回新的 state</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">currentState = <span class="hljs-title function_">currentReducer</span>(currentState, action)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">} <span class="hljs-keyword">finally</span> {isDispatching = <span class="hljs-literal">false</span>}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"><span class="hljs-keyword">const</span> listeners = (currentListeners = nextListeners)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// 遍历所有的监听函数</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < listeners.<span class="hljs-property">length</span>; i++) {<span class="hljs-keyword">const</span> listener = listeners[i]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-title function_">listener</span>() <span class="hljs-comment">// 执行每一个监听函数}</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"><span class="hljs-keyword">return</span> action</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">}</td></tr></tbody></table> |
这里 dispatch 主要做了二件事情
- 通过 reducer 更新 state
- 执行所有的监听函数,通知状态的变更
那么 reducer 是怎么改变 state 的呢?这就涉及到下面的 combineReducers 了
2、combineReducers
回到上面创建 store 的参数 reducers,
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > // 两个 reducer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">const</span> <span class="hljs-title function_">todos</span> = (<span class="hljs-params">state = INIT.todos, action</span>) => {<span class="hljs-comment">// ....};</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">const</span> <span class="hljs-title function_">filterStatus</span> = (<span class="hljs-params">state = INIT.filterStatus, action</span>) => {<span class="hljs-comment">// ...}</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">const</span> reducers = <span class="hljs-title function_">combineReducers</span>({</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">todos,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">filterStatus</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">});</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-comment">// 这是我们平常使用时创建 store</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">const</span> store = <span class="hljs-title function_">createStore</span>(reducers, state, enhance);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10">下面我们来看 combineReducers 做了什么</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">combineReducers</span>(<span class="hljs-params">reducers</span>) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-comment">// 第一次筛选,参数 reducers 为 Object</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"><span class="hljs-comment">// 筛选掉 reducers 中不是 function 的键值对</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-keyword">const</span> reducerKeys = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(reducers)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"><span class="hljs-keyword">const</span> finalReducers = {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < reducerKeys.<span class="hljs-property">length</span>; i++) {<span class="hljs-keyword">const</span> key = reducerKeys[i]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> reducers[key] === <span class="hljs-string">'function'</span>) {finalReducers[key] = reducers[key]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20"><span class="hljs-keyword">const</span> finalReducerKeys = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(finalReducers)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21"><span class="hljs-comment">// 二次筛选,判断 reducer 中传入的值是否合法(!== undefined)// 获取筛选完之后的所有 key</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22"><span class="hljs-keyword">let</span> shapeAssertionError</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"><span class="hljs-keyword">try</span> {<span class="hljs-title function_">assertReducerShape</span>(finalReducers)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24">} <span class="hljs-keyword">catch</span> (e) {shapeAssertionError = e}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25"><span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">combination</span>(<span class="hljs-params">state = {}, action</span>) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26"><span class="hljs-keyword">let</span> hasChanged = <span class="hljs-literal">false</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"><span class="hljs-keyword">const</span> nextState = {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28"><span class="hljs-comment">// 遍历所有的 key 和 reducer,分别将 reducer 对应的 key 所代表的 state,代入到 reducer 中进行函数调用</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < finalReducerKeys.<span class="hljs-property">length</span>; i++) {<span class="hljs-keyword">const</span> key = finalReducerKeys[i]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="30"><div class="hljs-ln-n" data-line-number="30"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="30"><span class="hljs-keyword">const</span> reducer = finalReducers[key]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="31"><div class="hljs-ln-n" data-line-number="31"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="31"><span class="hljs-comment">// 这里就是 reducer function 的名称和要和 state 同名的原因,传说中的黑魔法</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="32"><div class="hljs-ln-n" data-line-number="32"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="32"><span class="hljs-keyword">const</span> previousStateForKey = state[key]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="33"><div class="hljs-ln-n" data-line-number="33"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="33"><span class="hljs-keyword">const</span> nextStateForKey = <span class="hljs-title function_">reducer</span>(previousStateForKey, action)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="34"><div class="hljs-ln-n" data-line-number="34"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="34"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> nextStateForKey === <span class="hljs-string">'undefined'</span>) {<span class="hljs-keyword">const</span> errorMessage = <span class="hljs-title function_">getUndefinedStateErrorMessage</span>(key, action)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="35"><div class="hljs-ln-n" data-line-number="35"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="35"><span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(errorMessage)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="36"><div class="hljs-ln-n" data-line-number="36"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="36">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="37"><div class="hljs-ln-n" data-line-number="37"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="37"><span class="hljs-comment">// 将 reducer 返回的值填入 nextState</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="38"><div class="hljs-ln-n" data-line-number="38"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="38">nextState[key] = nextStateForKey</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="39"><div class="hljs-ln-n" data-line-number="39"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="39">hasChanged = hasChanged || nextStateForKey !== previousStateForKey</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="40"><div class="hljs-ln-n" data-line-number="40"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="40">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="41"><div class="hljs-ln-n" data-line-number="41"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="41"><span class="hljs-comment">// 发生改变了返回新的 nextState,否则返回原先的 state</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="42"><div class="hljs-ln-n" data-line-number="42"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="42"><span class="hljs-keyword">return</span> hasChanged ? nextState : state</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="43"><div class="hljs-ln-n" data-line-number="43"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="43">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="44"><div class="hljs-ln-n" data-line-number="44"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="44">}</td></tr></tbody></table> |
这里 reducer(previousStateForKey, action)执行的就是我们上面定义的 todos 和 filterStatus 方法。通过这二个 reducer 改变 state 值。
以前我一直很奇怪我们的 reducer 里的 state 是怎么做到取当前 reducer 对应的数据。看到 const previousStateForKey = state[key]这里我就明白了。
这里还有一个疑问点就是 combineReducers 的嵌套,最开始也我不明白,看了源码才知道 combineReducers()=> combination(state = {}, action),这里 combineReducers 返回的 combination 也是接受 (state = {}, action) 也就是一个 reducer 所以可以正常嵌套。
看到这初始化流程已经走完了。这个过程我们认识了 dispatch 和 combineReducers; 接下来我们来看一下我们自己要怎么更新数据。
用户更新数据时,是通过 createStore 后暴露出来的 dispatch 方法来触发的。dispatch 方法,是 store 对象提供的更改 currentState 这个闭包变量的唯一建议途径(注意这里是唯一建议途径,不是唯一途径,因为通过 getState 获取到的是 state 的引用,所以是可以直接修改的。但是这样就不能更新视图了)。正常情况下我们只需要像下面这样
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > //action creator</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">var</span> addTodo = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">return</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">type: <span class="hljs-string">'add_todo'</span>,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">text: text</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TodoReducer</span>(<span class="hljs-params">state = [], action</span>)</span>{<span class="hljs-keyword">switch</span> (action.type) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-keyword">case</span> <span class="hljs-string">'add_todo'</span>:</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-keyword">return</span> state.concat(action.text);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"><span class="hljs-keyword">default</span>:</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">return</span> state;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"><span class="hljs-comment">// 通过 store.dispatch(action) 来达到修改 state 的目的</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// 注意: 在 redux 里, 唯一能够修改 state 的方法, 就是通过 store.dispatch(action)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">store.dispatch({type: <span class="hljs-string">'add_todo'</span>, text: <span class="hljs-string">'读书'</span>});<span class="hljs-comment">// 或者下面这样</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-comment">// store.dispatch(addTodo('读书'));</span></td></tr></tbody></table> |
也就是说 dispatch 接受一个包含 type 的对象。框架为我们提供了一个创建 Action 的方法 bindActionCreators
3.bindActionCreators
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > // 下面来看下源码</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-comment">// 核心代码,并通过 apply 将 this 绑定起来</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">function</span> <span class="hljs-title function_">bindActionCreator</span>(<span class="hljs-params">actionCreator, dispatch</span>) {<span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<span class="hljs-keyword">return</span> <span class="hljs-title function_">dispatch</span>(actionCreator.<span class="hljs-title function_">apply</span>(<span class="hljs-variable language_">this</span>, <span class="hljs-variable language_">arguments</span>))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">bindActionCreators</span>(<span class="hljs-params">actionCreators, dispatch</span>) {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7"><span class="hljs-comment">// 如果 actionCreators 是一个函数,则说明只有一个 actionCreator,就直接调用 bindActionCreator</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> actionCreators === <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">return</span> <span class="hljs-title function_">bindActionCreator</span>(actionCreators, dispatch)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-comment">// 遍历对象,然后对每个遍历项的 actionCreator 生成函数,将函数按照原来的 key 值放到一个对象中,最后返回这个对象</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11"><span class="hljs-keyword">const</span> keys = <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(actionCreators)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">const</span> boundActionCreators = {}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < keys.<span class="hljs-property">length</span>; i++) {<span class="hljs-keyword">const</span> key = keys[i]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-keyword">const</span> actionCreator = actionCreators[key]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> actionCreator === <span class="hljs-string">'function'</span>) {boundActionCreators[key] = <span class="hljs-title function_">bindActionCreator</span>(actionCreator, dispatch)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18"><span class="hljs-keyword">return</span> boundActionCreators</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">}</td></tr></tbody></table> |
bindActionCreators 的作用就是使用 dispatch 把 action creator 包裹起来,这样我们就可以直接调用他们了。这个在平常开发中不常用。
4.applyMiddleware
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > // 最后我们回头来看一下之前调到的中间件,import thunkMiddleware from 'redux-thunk';</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-comment">// 两个 reducer</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3"><span class="hljs-keyword">const</span> <span class="hljs-title function_">todos</span> = (<span class="hljs-params">state = INIT.todos, action</span>) => {<span class="hljs-comment">// ....};</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">const</span> <span class="hljs-title function_">filterStatus</span> = (<span class="hljs-params">state = INIT.filterStatus, action</span>) => {<span class="hljs-comment">// ...};</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5"><span class="hljs-keyword">const</span> reducers = <span class="hljs-title function_">combineReducers</span>({</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="6"><div class="hljs-ln-n" data-line-number="6"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="6">todos,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="7"><div class="hljs-ln-n" data-line-number="7"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="7">filterStatus</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="8"><div class="hljs-ln-n" data-line-number="8"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="8">});</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="9"><div class="hljs-ln-n" data-line-number="9"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="9"><span class="hljs-comment">// 这是我们平常使用时创建 store</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="10"><div class="hljs-ln-n" data-line-number="10"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="10"><span class="hljs-keyword">const</span> store = <span class="hljs-title function_">createStore</span>(reducers, state, <span class="hljs-title function_">applyMiddleware</span>(thunkMiddleware));</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="11"><div class="hljs-ln-n" data-line-number="11"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="11">为了下文好理解这个放一下 redux-thunk 的源码</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="12"><div class="hljs-ln-n" data-line-number="12"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="12"><span class="hljs-keyword">function</span> <span class="hljs-title function_">createThunkMiddleware</span>(<span class="hljs-params">extraArgument</span>) {<span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">{ dispatch, getState}</span>) =></span> <span class="hljs-function"><span class="hljs-params">next</span> =></span> <span class="hljs-function"><span class="hljs-params">action</span> =></span> {<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> action === <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">return</span> <span class="hljs-title function_">action</span>(dispatch, getState, extraArgument);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-keyword">return</span> <span class="hljs-title function_">next</span>(action);</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15">};</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">const</span> thunk = <span class="hljs-title function_">createThunkMiddleware</span>();</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">thunk.<span class="hljs-property">withExtraArgument</span> = createThunkMiddleware;</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> thunk;</td></tr></tbody></table> |
可以看出 thunk 返回了一个接受 ({dispatch, getState}) 为参数的函数下面我们来看一下 applyMiddleware 源码分析
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-keyword" > export </span> <span class = "hljs-keyword" > default </span> <span class = "hljs-keyword" > function </span> <span class = "hljs-title function_" >applyMiddleware</span>(<span class = "hljs-params" >...middlewares</span>) {<span class = "hljs-keyword" > return </span> <span class = "hljs-function" ><span class = "hljs-params" >createStore</span> =></span> <span class = "hljs-function" >(<span class = "hljs-params" >...args</span>) =></span> {<span class = "hljs-keyword" >const</span> store = <span class = "hljs-title function_" >createStore</span>(...args)</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "2" ><div class = "hljs-ln-n" data-line-number= "2" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "2" ><span class = "hljs-keyword" > let </span> <span class = "hljs-title function_" >dispatch</span> = (<span class = "hljs-params" ></span>) => {</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "3" ><div class = "hljs-ln-n" data-line-number= "3" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "3" ><span class = "hljs-keyword" > throw </span> <span class = "hljs-keyword" > new </span> <span class = "hljs-title class_" >Error</span>(</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "4" ><div class = "hljs-ln-n" data-line-number= "4" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "4" ><span class = "hljs-string" >`Dispatching while constructing your middleware is not allowed. `</span> +</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "5" ><div class = "hljs-ln-n" data-line-number= "5" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "5" ><span class = "hljs-string" >`Other middleware would not be applied to this dispatch.`</span></td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "6" ><div class = "hljs-ln-n" data-line-number= "6" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "6" >)</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "7" ><div class = "hljs-ln-n" data-line-number= "7" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "7" >}</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "8" ><div class = "hljs-ln-n" data-line-number= "8" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "8" ><span class = "hljs-keyword" >const</span> middlewareAPI = {</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "9" ><div class = "hljs-ln-n" data-line-number= "9" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "9" ><span class = "hljs-attr" >getState</span>: store.<span class = "hljs-property" >getState</span>,</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "10" ><div class = "hljs-ln-n" data-line-number= "10" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "10" ><span class = "hljs-attr" >dispatch</span>: <span class = "hljs-function" >(<span class = "hljs-params" >...args</span>) =></span> <span class = "hljs-title function_" >dispatch</span>(...args)</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "11" ><div class = "hljs-ln-n" data-line-number= "11" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "11" >}</td></tr><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "12" ><div class = "hljs-ln-n" data-line-number= "12" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "12" ><span class = "hljs-comment" > // 每个 middleware 都以 middlewareAPI 作为参数进行注入,返回一个新的链。此时的返回值相当于调用 thunkMiddleware 返回的函数:(next) => (action) => {},接收一个 next 作为其参数</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="13"><div class="hljs-ln-n" data-line-number="13"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="13"><span class="hljs-keyword">const</span> chain = middlewares.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">middleware</span> =></span> <span class="hljs-title function_">middleware</span>(middlewareAPI))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="14"><div class="hljs-ln-n" data-line-number="14"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="14"><span class="hljs-comment">// 并将链代入进 compose 组成一个函数的调用链</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="15"><div class="hljs-ln-n" data-line-number="15"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="15"><span class="hljs-comment">// compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/ h 都是 chain 中的函数对象。// 在目前只有 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {}</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="16"><div class="hljs-ln-n" data-line-number="16"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="16"><span class="hljs-comment">// 之后以 store.dispatch 作为参数进行注入注意这里这里的 store.dispatch 是没有被修改的 dispatch 他被传给了 next;dispatch = compose(...chain)(store.dispatch)</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="17"><div class="hljs-ln-n" data-line-number="17"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="17"><span class="hljs-keyword">return</span> {</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="18"><div class="hljs-ln-n" data-line-number="18"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="18">...store,</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="19"><div class="hljs-ln-n" data-line-number="19"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="19">dispatch</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="20"><div class="hljs-ln-n" data-line-number="20"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="20">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="21"><div class="hljs-ln-n" data-line-number="21"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="21">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="22"><div class="hljs-ln-n" data-line-number="22"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="22">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="23"><div class="hljs-ln-n" data-line-number="23"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="23"><span class="hljs-comment">// 定义一个代码组合的方法</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="24"><div class="hljs-ln-n" data-line-number="24"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="24"><span class="hljs-comment">// 传入一些 function 作为参数,返回其链式调用的形态。例如,// compose(f, g, h) 最终返回 (...args) => f(g(h(...args)))</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="25"><div class="hljs-ln-n" data-line-number="25"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="25"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">compose</span>(<span class="hljs-params">...funcs</span>) {<span class="hljs-keyword">if</span> (funcs.<span class="hljs-property">length</span> === <span class="hljs-number">0</span>) {<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-params">arg</span> =></span> arg} <span class="hljs-keyword">else</span> {<span class="hljs-keyword">const</span> last = funcs[funcs.<span class="hljs-property">length</span> - <span class="hljs-number">1</span>]</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="26"><div class="hljs-ln-n" data-line-number="26"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="26"><span class="hljs-keyword">const</span> rest = funcs.<span class="hljs-title function_">slice</span>(<span class="hljs-number">0</span>, -<span class="hljs-number">1</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="27"><div class="hljs-ln-n" data-line-number="27"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="27"><span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">...args</span>) =></span> rest.<span class="hljs-title function_">reduceRight</span>(<span class="hljs-function">(<span class="hljs-params">composed, f</span>) =></span> <span class="hljs-title function_">f</span>(composed), <span class="hljs-title function_">last</span>(...args))</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="28"><div class="hljs-ln-n" data-line-number="28"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="28">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="29"><div class="hljs-ln-n" data-line-number="29"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="29">}</td></tr></tbody></table> |
我第一眼看去一脸闷逼,咋这么复杂。但是静下心来看也就是一个三级柯里化的函数,我们从头来分析一下这个过程
1 | <table class = "hljs-ln" ><tbody><tr><td class = "hljs-ln-line hljs-ln-numbers" data-line-number= "1" ><div class = "hljs-ln-n" data-line-number= "1" ></div></td><td class = "hljs-ln-line hljs-ln-code" data-line-number= "1" ><span class = "hljs-comment" > // createStore.js</span></td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="2"><div class="hljs-ln-n" data-line-number="2"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="2"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> enhancer !== <span class="hljs-string">'undefined'</span>) {<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> enhancer !== <span class="hljs-string">'function'</span>) {<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">'Expected the enhancer to be a function.'</span>)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="3"><div class="hljs-ln-n" data-line-number="3"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="3">}</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="4"><div class="hljs-ln-n" data-line-number="4"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="4"><span class="hljs-keyword">return</span> <span class="hljs-title function_">enhancer</span>(createStore)(reducer, preloadedState)</td></tr><tr><td class="hljs-ln-line hljs-ln-numbers" data-line-number="5"><div class="hljs-ln-n" data-line-number="5"></div></td><td class="hljs-ln-line hljs-ln-code" data-line-number="5">}</td></tr></tbody></table> |
也就是说,会变成这样
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
applyMiddleware(thunkMiddleware)
applyMiddleware 接收 thunkMiddleware 作为参数,返回形如 (createStore) => (…args) => {} 的函数。
applyMiddleware(thunkMiddleware)(createStore)
以 createStore 作为参数,调用上一步返回的函数 (…args) => {}
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
以(reducer, preloadedState)为参数进行调用。在这个函数内部,thunkMiddleware 被调用,其作用是监测 type 是 function 的 action
因此,如果 dispatch 的 action 返回的是一个 function,则证明是中间件,则将 (dispatch, getState) 作为参数代入其中,进行 action 内部下一步的操作。否则的话,认为只是一个普通的 action,将通过 next(也就是 dispatch)进一步分发
也就是说,applyMiddleware(thunkMiddleware)作为 enhance,最终起了这样的作用:
对 dispatch 调用的 action 进行检查,如果 action 在第一次调用之后返回的是 function,则将 (dispatch, getState) 作为参数注入到 action 返回的方法中,否则就正常对 action 进行分发,这样一来我们的中间件就完成喽~
因此,当 action 内部需要获取 state,或者需要进行异步操作,在操作完成之后进行事件调用分发的话,我们就可以让 action 返回一个以 (dispatch, getState) 为参数的 function 而不是通常的 Object,enhance 就会对其进行检测以便正确的处理
到此 redux 源码的主要部分讲完了,如有感兴趣的同学可以去看一下我没讲到的一些东西转送门 redux;