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

Redux用法与原理

时间:2023-03-29 11:07:10 HTML

1.概述本文带大家回顾一下redux的用法、reduxAPI、createStore、reducer、dispatch等,了解了用法之后,我们一起来探究一下原理。难点是redux中间件的原理部分。注意:本文只探讨redux,不探讨react-redux。后面会有专门的文章讲解react-redux的使用和原理。redux的使用不限于react。任何想订阅事件的地方,都可以单独使用redux。2.redux的设计思想是将整个应用状态存储在一个地方,称为store,在单一数据源中存储一棵状态树。state将dispatchbehavior动作分发给store,而不是直接改变state。只读用户订阅)store,当有dispatch行为时,会通知订阅者更新redux进程。处理了两件事,一是调用reducer更新状态,二是触发之前订阅的事件的执行,仅此而已。3.redux的基本使用本文着重介绍原理,适合有基本redux基础的同学,简单回顾一下使用方法。3.1createStore用于从'redux'创建storeimport{createStore};constdefaultState=0;constreducer=(state=defaultState,action)=>{switch(action.type){case'ADD':returnstate+action.payload;默认值:返回状态;}};conststore=createStore(reducer);3.2getState获取当前状态数据conststate=store.getState();3.3actionaction是一个对象。type属性是必需的,表示动作的名称。其他属性可以自由设置。constaction={type:'ADD_TODO',payload:'LearnRedux'};3.3dispatchdispatchaction的唯一方式import{createStore}from'redux';conststore=createStore(fn);store.dispatch({type:'ADD_TODO',payload:'LearnRedux'});3.4reducer接受旧的state和action作为参数,返回新的stateconstdefaultState=0;constreducer=(state=defaultState,action)=>{switch(action.type){case'ADD':returnstate+action.payload;默认值:返回状态;}};3.5subscribe设置订阅,所有订阅的事件会在dispatchimport{createStore}from'redux'时自动执行;conststore=createStore(reducer);letunsubscribe=store.subscribe(()=>{console.log("stateupdated")});//subscribe的返回值可以unsubscribe//unsubscribe();3.6combineReducers将多个reducer合二为一,因为在实际项目中,不可能将所有不同的业务数据写在一个reducer中,并且维护成本高。通常reducer是按业务分开的,最后通过combineReducers组合,然后从'redux'传给createStoreimport{combineReducers};constreducer=combineReducers({chatLog,statusMessage,userName})导出默认减速器;4.原理我看到的redux版本是4.1.2的源码4.1createStore源码分析/***createstore*@param{*}reducer*@param{*}preloadedState*@param{*}enhancer*@returns*/unctioncreateStore(reducer,preloadedState,enhancer){if(typeofpreloadedState==='函数'&&typeofenhancer==='undefined'){enhancer=preloadedState;预加载状态=未定义;}if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError(process.env.NODE_ENV==="production"?formatProdErrorMessage(1):"Expectedtheenhancer成为一个函数。相反,收到:'"+kindOf(enhancer)+"'");}returnenhancer(createStore)(reducer,preloadedState);}//前面讲了redux中间件,这里只关注createStore的第一个参数reducer//reducer必须是函数if(typeofreducer!=='function'){thrownewError("Expectedtherootreducer是一个函数。");}//定义内部变量varcurrentReducer=reducer;//当前状态数据,初始值为preloadedStatevarcurrentState=preloadedState;//保存所有订阅函数varcurrentListeners=[];//保存订阅函数快照varnextListeners=currentListeners;//dispatch是否正在执行varisDispatching=false;//在执行订阅时提供备份,参见functionensureCanMutateNextListeners(){if(nextListeners===currentListeners){nextListeners=currentListeners.slice();}}/***getState源码,获取当前最新状态*@returnsreturnstate*/functiongetState(){returncurrentState;}/***subscribe源码,用于收集订阅*@param{*}监听器订阅方法*@returns返回取消本次订阅的方法*/functionsubscribe(listener){//监听器必须是一个函数if(typeoflistener!=='function'){thrownewError("Expectedthelistenertobeafunction");}//闭包变量,用于标识取消订阅方法只能执行一次varisSubscribed=true;//为每个订阅提供快照备份nextListeners,//防止在dispatch遍历currentListeners的过程中触发订阅/取消订阅功能//如果直接更新currentListeners,会混淆当前循环体的逻辑,//因此,所有订阅/未订阅的监听器都存储在nextListeners中,不会影响当前的dispatch(action)ensureCanMutateNextListeners();nextListeners.push(监听器);returnfunctionunsubscribe(){//已经取消,直接跳出if(!isSubscribed){return;}//取消成功,没有该订阅isSubscribed=false;ensureCanMutateNextListeners();varindex=nextListeners.indexOf(监听器);nextListeners.splice(index,1);当前监听器=null;};}/***dispatchsourcecode*@param{*}actionacceptsactionobject*@returnsreturnsaction*/functiondispatch(action){//action是一个普通对象if(!isPlainObject(action)){thrownewError(“动作必须是普通对象。”);}//动作对象必须有类型属性if(typeofaction.type==='undefined'){thrownewError('动作可能没有未定义的“类型”属性。你可能错过了动作类型字符串常量。');}//执行reducer时不允许有其他dispatch操作if(isDispatching){thrownewError('Reducers可能不会调度动作。');}try{//开始执行reducerisDispatching=true;//reducer接受当前状态和action对象,返回新状态currentState=currentReducer(currentState,action);}finally{//reducer执行完成,状态更新isDispatching=false;}//然后执行所有订阅的监听器varlisteners=currentListeners=nextListeners;//遍历执行for(vari=0;i