You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportdefaultfunctioncreateStore(reducer,preloadedState,enhancer){// 如果第二个参数没有传入初始的state,而是传入了enhancer(为applyMiddleware调用的返回值), 那就将第二个参数,即preloadedState赋值给enhancerif(typeofpreloadedState==='function'&&typeofenhancer==='undefined'){enhancer=preloadedStatepreloadedState=undefined}// 如果传入了enhancer,但enhancer不是一个函数,报错if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError('Expected the enhancer to be a function.')}// 反之, 执行。注意此处。此处意味着,如果createStore的时候传入了enhancer,是会将createStore传入enhancer中,执行enhancer, 而enhancer的返回值也是一个函数。具体的可以等到下面我们讲解applyMiddleware,看完你就知道到底发生了什么。returnenhancer(createStore)(reducer,preloadedState)}// 如果没传入enhancer,就继续下面的逻辑// reducer是要求为一个函数的,如果不是一个函数,报错if(typeofreducer!=='function'){thrownewError('Expected the reducer to be a function.')}
.....
.....// 最后createStore就会返回dispatch,subscribe, getState等几个常用的apireturn{
dispatch,
subscribe,
getState,
replaceReducer,[$$observable]: observable};}
functiongetState(){// 如果正在dispatch的话, 说明新的state正在计算中,现在的state是旧的,为了确保用户能获得新的// state,所以要加一个判断,如果是正在dispatch的话,就报错,反之,返回现在的stateif(isDispatching){thrownewError('You may not call store.getState() while the reducer is executing. '+'The reducer has already received the state as an argument. '+'Pass it down from the top reducer instead of reading it from the store.')}returncurrentState}
functionsubscribe(listener){// listener是state变化时的回调,必须是个函数if(typeoflistener!=='function'){thrownewError('Expected the listener to be a function.')}// 如果是正在dispatch中,就报错。因为要确保state变化时,监听器的队列也必须是最新的。所以监听器的注册要在计算新的state之前。if(isDispatching){thrownewError('You may not call store.subscribe() while the reducer is executing. '+'If you would like to be notified after the store has been updated, subscribe from a '+'component and invoke store.getState() in the callback to access the latest state. '+'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.')}// 标志是否注册,额,其实个人感觉没啥必要。不过仔细想想,应该是防止用户多次调用取消监听的函数。letisSubscribed=true// 其实这个函数就是判断当前的监听器队列和未来的是否一样,如果不一样那就将当前的赋值给未来的,额,还是不是很理解为什么得这么实现,可能是为了达到数据不可变的效果,避免压进新的回调时,导致当前的监听器队列也有这个回调ensureCanMutateNextListeners()// 将回调压进未来的监听器队列中nextListeners.push(listener)// 注册监听器后会返回一个取消监听的函数returnfunctionunsubscribe(){// 如果是已经调用该函数取消监听了,就返回if(!isSubscribed){return}if(isDispatching){thrownewError('You may not unsubscribe from a store listener while the reducer is executing. '+'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.')}// 标志已经取消了isSubscribed=falseensureCanMutateNextListeners()// 删除constindex=nextListeners.indexOf(listener)nextListeners.splice(index,1)}}
functiondispatch(action){// action要求是一个简单对象,而一个简单对象就是指通过对象字面量和new Object()创建的对象,如果不是就报错。if(!isPlainObject(action)){thrownewError('Actions must be plain objects. '+'Use custom middleware for async actions.')}// reducer内部是根据action的type属性来switch-case,决定用什么逻辑来计算state的,所以type属性是必须的。if(typeofaction.type==='undefined'){thrownewError('Actions may not have an undefined "type" property. '+'Have you misspelled a constant?')}// 如果是已经在dispatch的,就报错,避免不一致if(isDispatching){thrownewError('Reducers may not dispatch actions.')}// 这里就是计算新的state,并赋值给currentStatetry{isDispatching=truecurrentState=currentReducer(currentState,action)}finally{isDispatching=false}// state更新了后,就如之前我们所说的subscribe,将注册的回调都触发一遍。大家要注意这里,是都触发一遍哦!这个点了解,react-redux的一些原理会比较容易理解。constlisteners=(currentListeners=nextListeners)for(leti=0;i<listeners.length;i++){constlistener=listeners[i]listener()}returnaction}
if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError('Expected the enhancer to be a function.')}returnenhancer(createStore)(reducer,preloadedState)}
背景
最近不想写业务代码了,因为就得去实习了。所以打算开始补补坑。比如自己阅读源码的计划。所以今天来聊聊redux的源码。后续会有redux-thunk和react-redux的源码阅读。搞定这些的话,就开始阅读一个node的库的源码了,比如eventproxy和anywhere。
开始
总览, redux的文件结构
文件看起来貌似不少,其实,要理解redux的内部实现,主要就看 createStore.js
,applyMiddleware.js ,combineReducers.js和compose.js。下面从createStore.js开始看。
createStore.js
上面的代码给大家展览了下createStore这个函数大概做了什么,其实就是封装了一些api,最后暴露给用户使用。接下来看一下各个api的实现:
先看一下私有变量的定义
getState : 用来获取store中的state的。因为redux是不允许用户直接操作state,对于state的获取,是得通过getState的api来获取store内部的state。
subscribe :redux提供了用户一个监听state变化的api,这个尤为重要,如果没有这个api的暴露,react-redux应该就比较实现了。
dispatch : 该函数是与getState对应的,getState是读,那dispatch就是写。redux的改动state,只能通过发起一个dispatch,传达一个action给reducer,reducer会根据action和currentState以及自己的内部实现逻辑,来计算出新的state,从而达到写的目的。
以上就是createStore的大致实现。这个函数难度不大,更多的是一个了解redux的入口。咱们从这个入口来一点点挖掘别的代码的实现。下面先从combineReducers开始
combineReducers
这个整合的过程就是将所有的reducer存在一个对象里。当dispatch一个action的时候,通过遍历每一个reducer, 来计算出每个reducer的state, 其中用到的优化就是每遍历一个reducer就会判断新旧的state是否发生了变化, 最后决定是返回旧state还是新state。最后得到的state的数据结构类似存reducer的数据结构,就是键为reducer的名字,值为对应reducer的值。这个部分其实也不难。下面继续,我们看看applyMiddleware的实现
applyMiddleware
这个部分就是用来扩展redux的功能的。因为redux的最原始的功能就是操作state,管理state。如果我们需要在这个基础上,根据需求扩展一些功能,就需要通过使用别人编写好的中间件,或者自己编写的中间件来达到需求。比如,发起一个dispatch时,我们为了方便调试,不愿意每次自己手动console.log出这个action,这个时候编写一个logger中间件,就可以自动打印出每次dispatch发起的action,就很容易方便我们测试。又比如,我们要处理异步的action,就可以使用redux-thunk和redux-saga这类中间件。总之,该函数为我们提供了无限的可能。
我们一点点来看代码:
先看个总览的,注意到applyMiddleware接受不定数量的中间件,然后返回一个接受一个creatStore作为参数,返回一个函数的函数。还记得我们在creatStore的时候么?那里有个场景就是
当enhancer不为空且为函数时,就执行该函数,并return回去,作为creatStore的返回值。而这个enhancer是什么呢?翻看redux的文档:
哦!enhancer就是applyMiddleware的结果,就是一个 creatStore => (...args) = {}的函数。那看下enhancer的代码:
跟着上面的注释,大家应该能弄懂enhancer的原理。我这里总结一下,enhancer接收一个creatStore,会在内部创建一个store,然后对该store进行增强,增强的部位在于dispatch。增强的具体方式是通过compose来构造一个dispatch链,链的具体形式就是**[中间件1,中间件2, ......, 中间件N, store.dispatch]** ,然后将增强的dispatch作为store新的dispatch暴露给用户。那用户每次dispatch的时候,就会依次执行每个中间件,执行完当前的,会将执行权交给下一个,直到reducer中,计算出新的state。
结语
网上讲解redux的源码很多,我这篇也是其中一个,主要是我个人学习源码后的,一种记录方式,加深自己印象,也为了之后忘了可以快速重温。redux其实实现上不难,但是思想上真是精髓。程序员的编码能力是一个刚需,但是设计思想是要借他山之玉,来攻石的。站在巨人的肩膀上看远方,希望自己多阅读他人的源码,在能了解原理更好运用的同时,以后自己也能创造出好用的轮子。谢谢大家花时间观看。另外,附源码地址:https://github.com/Juliiii/source-plan ,欢迎大家star和fork ,也欢迎大家和我讨论 。
The text was updated successfully, but these errors were encountered: