当前位置: 首页 > Web前端 > HTML

深入理解Redux的原理,实现一个redux

时间:2023-03-29 12:57:25 HTML

Redux的作用是什么?Redux的作用是实现状态传递和状态管理。这里你可能会说,如果是状态传递,那么我的道具传递也能达到这样的效果吗?contextscheme不也能达到这个效果吗?没错,是的,但是以上两种方案都有局限性。props方案仅适用于在父子组件之间传递状态。虽然上下文上下文方案可以在根组件上定义上下文,但是有两个缺点。只要上下文中的状态发生变化,就会重新渲染整个组件树,这会产生巨大的性能开销。组件逻辑与状态的耦合度太高,不利于解耦,即无法实现状态的统一管理。既然Redux的作用是管理和传递状态,那它的作用场景呢?当然你可以根据上面提到的两种方案选择使用Redux。Redux的本质是全局变量是协调和管理的。如果涉及多个状态,多个状态会被多个组件使用,比如商场中的购物车场景,可以毫不犹豫的考虑使用Redux。如果涉及到多个状态,但是虽然状态很多,但是只用到组件,或者用到相关组件,就不需要用Redux了。如果没有那么多状态,那么你就不需要使用Redux。另外,Redux还有一个好处就是不仅可以使用React本身,还可以使用其他的框架,比如jQuery、kerry_dom、vue等,但是相比vue,vue有自己更好的状态管理库vuex,嗯,废话不多说,我们先来看看Redux在项目中是如何使用的。Redux的使用//store.jsimport{createStore}from"redux";importreducerfrom"./reducer";exportdefaultcreateStore(reducer);//reducer.jsimport{cloneDeep}from'lodash';constinitilaValue={count:0};constreducer=(state=initilaValue,action)=>{state=cloneDeep(state)const{type,payload}=action;switch(type){case"add":state.count+=payload;休息;case"reduce":state.count-=payload;breakdefault:}returnstate;};exportdefaultreducer;//App.jsimportReact,{Component}from'react';importstorefrom"./store";导出默认类AppextendsComponent{componentDidMount(){//reducer不会触发页面变化,需要state来触发store.subscribe(()=>{this.setState(store.getState())})}render(){//获取reducer数据const{count}=store.getState()返回(

-
{count}+
分区>);}reduce=()=>{//通知reducer页面数据变化store.dispatch({type:'reduce',payload:1})}add=()=>{//通知reducer页面数据变化store.dispatch({type:'add',payload:1})}}以上代码可以实现count的加减法。我们可以看到在store.js文件中的createStore有几个需要注意的地方。cloneDeep,returnstate,state=initialValue在reducer.js文件中。App.js文件中的dispatch、getState、type、payload。很明显,createStore的作用是创建一个仓库,getState是获取当前状态值,dispatch是调度到store进行某个操作后更新某个状态,type是具体的交互,payload是具体的内容每一次互动。同学们可以看到我在reducer里面做了一个state的deepclone,这是为什么呢?这是因为在每个动作中我们都得到相同状态的内存地址。我们的期望是不管你在switch里面怎么改state,但是我这一步不想改publicstate的count,onlyin我只会在return的时候改真正的publicstate,也就是比方说,由执行reducer函数生成的私有闭包中的公共状态信息。state=initialValue这一步的操作是,第一次dispatch的时候,reducer收到的state是空的,我们给它赋基本值。了解了这么多,我们来看看他的源码是如何实现的。Redux源码//Redux/redux/src/index.tsexport{createStore,//创建仓库apicombineReducers,//MergeReducerbindActionCreators,//TransformactionapplyMiddleware,//中间件应用方案compose,//函数组合__DO_NOT_USE__ActionTypes}上面index.ts文件夹中暴露了几个API,我们主要看createStore。导出默认函数createStore(reducer:Reducer,preloadedState?:PreloadedState|StoreEnhancer,enhancer?:StoreEnhancer):Store,A,StateExt,Ext>&Ext{//createStore函数不支持第二个、第三个和第四个参数作为函数//参见https://redux.js.org/tutorials/fundamentals/part-4-store#creating-详情商店-with-enhancersif((typeofpreloadedState==='function'&&typeofenhancer==='function')||(typeofenhancer==='function'&&typeofarguments[3]==='function')){thrownewError(...)}//第二个参数是函数,不传第三个参数if(typeofpreloadedState==='function'&&typeofenhancer==='undefined'){enhancer=preloadedStateasStoreEnhancerpreloadedState=undefined}//有第三个参数并且不是函数if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){抛出新错误(`预计增强器是一个函数。相反,收到:'${kindOf(enhancer)}'`)}returnenhancer(createStore)(reducer,preloadedStateasPreloadedState)asStore,A,StateExt,Ext>&Ext}//如果reducer不是函数,要报错}letcurrentReducer=reducer//当前的reducerletcurrentState=preloadedStateasS//当前的stateletcurrentListeners:(()=>void)[]|null=[]//事件池letnextListeners=currentListenersletisDispatching=false//正在派发/***这会生成currentListeners的浅表副本,因此我们可以在调度时将nextListeners用作临时列表。**这可以防止消费者在调度过程中调用*订阅/取消订阅时出现任何错误。*/函数ensureCanMutateNextListeners(){if(nextListeners===currentListeners){nextListeners=currentListeners.slice()}}//返回当前状态functiongetState():S{if(isDispatching){thrownewError(...)}returncurrentStateasS}//向事件池添加一个更新事件functionsubscribe(listener:()=>void){//检查是否是一个函数if(typeoflistener!=='function'){thrownewError(...'+'组件并在回调中调用store.getState()以访问最新状态。'+'有关详细信息,请参阅https://redux.js.org/api/store#subscribelistener。')}letisSubscribed=true//避免重复添加ensureCanMutateNextListeners()nextListeners.push(listener)//订阅函数一旦执行,将返回取消订阅以从事件池中删除当前事件)//移除当前事件constindex=nextListeners.indexOf(listener)nextListeners.splice(index,1)currentListeners=null}}//调度函数functiondispatch(action:A){//如果传递的动作不是objectErrorif(!isPlainObject(action)){thrownewError(...)}//每个action都需要一个type字段,如果没有则报错if(typeofaction.type==='undefined'){thrownewError(...)}//Dispatching..if(isDispatching){thrownewError('Reducersmaynotdispatchactions.')}try{isDispatching=true//执行reducer并改变状态currentState=currentReducer(currentState,action)}finally{isDispatching=false}//dispatch通知事件池执行事件,遍历exec行动constlisteners=(currentListeners=nextListeners)for(leti=0;i(nextReducer:Reducer):Store,NewActions,StateExt,Ext>&Ext{//输入参数不是函数,会报错if(typeofnextReducer!=='function'){thrownewError(...)}...//需要在开始的时候派发同步状态,其中{type:ActionTypes.REPLACE}是唯一标识//如果是不是唯一的,它可能在开始时被销毁,StateExt>,NewActions,StateExt,Ext>&Ext}...//在初始化store的时候,需要调度一个同步状态调度({类型:ActionTypes.INIT}一个sA)conststore={dispatch:dispatchasDispatch,subscribe,getState,replaceReducer,[$$observable]:observable}作为未知的Store,A,StateExt,Ext>&Ext//返回仓库conststore=createStore({count:0})returnstore}的确,redux的实现仅仅用了几百行代码。为此,我们也实现了一个简单版的redux来致敬。我们的redux只实现了getState、dispatch、createStore方法//myReduximport{cloneDeep}from'lodash'exportfunctioncreateStore(reducer){if(typeofreducer!=='function'){thrownewError('reducermustbeanfunction')}letstate,listeners=[];constgetState=()=>{//深克隆一个statereturncloneDeep(state);}constsubscribe=(listener)=>{if(typeoflistener!=='function'){thrownewError('listenermustbeanfunction')}//去重if(!listeners.includes(listener)){listeners.push(listener)}//源码里执行订阅,返回一个负载函数returnfunctionunsubscribe(){listeners.filter(action=>action!==listener)}}constdispatch=(action)=>{if(typeofaction!=='object'&&action!==null){thrownewError('actionmustbeaobject')}//判断有没有typeif(typeofaction.type===undefined){thrownewError('action.typemustexistence')}//执行try{state=reducer(state,action)}catch(error){//...}//通知事件池中的方法执行listeners.forEach(listener=>{if(typeoflistener==='function'){listener();}})}//第一次进来dispatch一次,同步初始状态dispatch({type:typeofSymbol!==undefined?Symbol('ABC'):'uniquevalue'})//暴露方法return{getState,dispatch,subscribe}}refer到前端进度面试题分步详解大家可以去codeSandBox练习一下。当然,这只是redux的一个简单版本。官方推荐在实际项目开发中使用react-redux,因为它只关注数据管理,总结了redux的大体工作流程。: