怎么理解和掌握Redux
导读:本文共5142字符,通常情况下阅读需要17分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 一、Why Redux在说为什么用 Redux 之前,让我们先聊聊组件通信有哪些方式。常见的组件通信方式有以下几种:父子组件:props、state/callback回调来进行通信单页面应用:路由传值全局事件比如EventEmitter监听回调传值react中跨层级组件数据传递Context(上下文)在小型、不太复杂的应用中,一般用以上几种组件通信方式基本就足够... ...
目录
(为您整理了一些要点),点击可以直达。一、Why Redux
在说为什么用 Redux 之前,让我们先聊聊组件通信有哪些方式。常见的组件通信方式有以下几种:
父子组件:props、state/callback回调来进行通信
单页面应用:路由传值
全局事件比如EventEmitter监听回调传值
react中跨层级组件数据传递Context(上下文)
在小型、不太复杂的应用中,一般用以上几种组件通信方式基本就足够了。
但随着应用逐渐复杂,数据状态过多(比如服务端响应数据、浏览器缓存数据、UI状态值等)以及状态可能会经常发生变化的情况下,使用以上组件通信方式会很复杂、繁琐以及很难定位、调试相关问题。
因此状态管理框架(如 Vuex、MobX、Redux等)就显得十分必要了,而 Redux 就是其中使用最广、生态最完善的。
二、Redux Data flow
在一个使用了 Redux 的 App应用里面会遵循下面四步:
第一步:通过store.dispatch(action)来触发一个action,action就是一个描述将要发生什么的对象。如下:
bindActionCreator就是将发送actions的过程简化,当调用这个返回的函数时就自动调用dispatch,发送对应的action。
bindActionCreators根据不同类型的actionCreators做不同的处理,actionCreators是函数就返回函数,是对象就返回一个对象。主要是将actions转化为dispatch(action)格式,方便进行actions的分离,并且使代码更加简洁。
5、compose.js
/***Composessingle-argumentfunctionsfromrighttoleft.Therightmost*functioncantakemultipleargumentsasitprovidesthesignaturefor*theresultingcompositefunction.**@param{...Function}funcsThefunctionstocompose.*@returns{Function}Afunctionobtainedbycomposingtheargumentfunctions*fromrighttoleft.Forexample,compose(f,g,h)isidenticaltodoing*(...args)=>f(g(h(...args))).*/exportdefaultfunctioncompose(...funcs){if(funcs.length===0){returnarg=>arg}if(funcs.length===1){returnfuncs[0]}returnfuncs.reduce((a,b)=>(...args)=>a(b(...args)))}
compose是函数式变成里面非常重要的一个概念,在介绍compose之前,先来认识下什么是 Reduce?官方文档这么定义reduce:reduce()方法对累加器和数组中的每个元素(从左到右)应用到一个函数,简化为某个值。compose是柯里化函数,借助于Reduce来实现,将多个函数合并到一个函数返回,主要是在middleware中被使用。
6、applyMiddleware.js
/***Createsastoreenhancerthatappliesmiddlewaretothedispatchmethod*oftheReduxstore.Thisishandyforavarietyoftasks,suchasexpressing*asynchronousactionsinaconcisemanner,orloggingeveryactionpayload.*/exportdefaultfunctionapplyMiddleware(...middlewares){returncreateStore=>(...args)=>{conststore=createStore(...args)......return{...store,dispatch}}}
applyMiddleware.js文件提供了middleware中间件重要的API,middleware中间件主要用来对store.dispatch进行重写,来完善和扩展dispatch功能。
那为什么需要中间件呢?
首先得从Reducer说起,之前 Redux三大原则里面提到了reducer必须是纯函数,下面给出纯函数的定义:
对于同一参数,返回同一结果
结果完全取决于传入的参数
不产生任何副作用
至于为什么reducer必须是纯函数,可以从以下几点说起?
因为 Redux 是一个可预测的状态管理器,纯函数更便于 Redux进行调试,能更方便的跟踪定位到问题,提高开发效率。
Redux 只通过比较新旧对象的地址来比较两个对象是否相同,也就是通过浅比较。如果在 Reducer 内部直接修改旧的state的属性值,新旧两个对象都指向同一个对象,如果还是通过浅比较,则会导致 Redux 认为没有发生改变。但要是通过深比较,会十分耗费性能。最佳的办法是 Redux返回一个新对象,新旧对象通过浅比较,这也是 Reducer是纯函数的重要原因。
Reducer是纯函数,但是在应用中还是会需要处理记录日志/异常、以及异步处理等操作,那该如何解决这些问题呢?
这个问题的答案就是中间件。可以通过中间件增强dispatch的功能,示例(记录日志和异常)如下:
conststore=createStore(reducer);constnext=store.dispatch;//重写store.dispatchstore.dispatch=(action)=>{try{console.log('action:',action);console.log('currentstate:',store.getState());next(action);console.log('nextstate',store.getState());}catch(error){console.error('msg:',error);}}
五、从零开始实现一个简单的Redux
既然是要从零开始实现一个Redux(简易计数器),那么在此之前我们先忘记之前提到的store、Reducer、dispatch等各种概念,只需牢记Redux是一个状态管理器。
首先我们来看下面的代码:
letstate={count:1}//修改之前console.log(state.count);//修改count的值为2state.count=2;//修改之后console.log(state.count);
我们定义了一个有count字段的state对象,同时能输出修改之前和修改之后的count值。但此时我们会发现一个问题?就是其它如果引用了count的地方是不知道count已经发生修改的,因此我们需要通过订阅-发布模式来监听,并通知到其它引用到count的地方。因此我们进一步优化代码如下:
letstate={count:1};//订阅functionsubscribe(listener){listeners.push(listener);}functionchangeState(count){state.count=count;for(leti=0;i<listeners.length;i++){constlistener=listeners[i];listener();//监听}}
此时我们对count进行修改,所有的listeners都会收到通知,并且能做出相应的处理。但是目前还会存在其它问题?比如说目前state只含有一个count字段,如果要是有多个字段是否处理方式一致。同时还需要考虑到公共代码需要进一步封装,接下来我们再进一步优化:
constcreateStore=function(initState){letstate=initState;//订阅functionsubscribe(listener){listeners.push(listener);}functionchangeState(count){state.count=count;for(leti=0;i<listeners.length;i++){constlistener=listeners[i];listener();//通知}}functiongetState(){returnstate;}return{subscribe,changeState,getState}}
我们可以从代码看出,最终我们提供了三个API,是不是与之前Redux源码中的核心入口文件index.js比较类似。但是到这里还没有实现Redux,我们需要支持添加多个字段到state里面,并且要实现Redux计数器。
letinitState={counter:{count:0},info:{name:'',description:''}}letstore=createStore(initState);//输出countstore.subscribe(()=>{letstate=store.getState();console.log(state.counter.count);});//输出infostore.subscribe(()=>{letstate=store.getState();console.log(`${state.info.name}:${state.info.description}`);});
通过测试,我们发现目前已经支持了state里面存多个属性字段,接下来我们把之前changeState改造一下,让它能支持自增和自减。
//自增store.changeState({count:store.getState().count+1});//自减store.changeState({count:store.getState().count-1});//随便改成什么store.changeState({count:金融});
我们发现可以通过changeState自增、自减或者随便改,但这其实不是我们所需要的。我们需要对修改count做约束,因为我们在实现一个计数器,肯定是只希望能进行加减操作的。所以我们接下来对changeState做约束,约定一个plan方法,根据type来做不同的处理。
functionplan(state,action)=>{switch(action.type){case'INCREMENT':return{...state,count:state.count+1}case'DECREMENT':return{...state,count:state.count-1}default:returnstate}}letstore=createStore(plan,initState);//自增store.changeState({type:'INCREMENT'});//自减store.changeState({type:'DECREMENT'});
我们在代码中已经对不同type做了不同处理,这个时候我们发现再也不能随便对state中的count进行修改了,我们已经成功对changeState做了约束。我们把plan方法做为createStore的入参,在修改state的时候按照plan方法来执行。到这里,恭喜大家,我们已经用Redux实现了一个简单计数器了。
这就实现了 Redux?这怎么和源码不一样啊
然后我们再把plan换成reducer,把changeState换成dispatch就会发现,这就是Redux源码所实现的基础功能,现在再回过头看Redux的数据流图是不是更加清晰了。
六、Redux Devtools
Redux devtools是Redux的调试工具,可以在Chrome上安装对应的插件。对于接入了Redux的应用,通过 Redux devtools可以很方便看到每次请求之后所发生的改变,方便开发同学知道每次操作后的前因后果,大大提升开发调试效率。
如上图所示就是 Redux devtools的可视化界面,左边操作界面就是当前页面渲染过程中执行的action,右侧操作界面是State存储的数据,从State切换到action面板,可以查看action对应的 Reducer参数。切换到Diff面板,可以查看前后两次操作发生变化的属性值。
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
怎么理解和掌握Redux的详细内容,希望对您有所帮助,信息来源于网络。