前言Redux是react开发入门的必经之路,也是react项目中使用最多的状态管理库。虽然我们可以在不使用redux的情况下,通过react的state和parent-childprops来做基本的数据通信和项目开发,但是对于一个大型的项目,我们往往更多的是考虑代码结构和组件之间的通信。我们需要一种非常优雅和可扩展的方式来开发我们复杂的系统,所以在这种情况下使用redux是最好的选择。因为有小伙伴想快速上手redux开发,所以笔者特地开发了一个小项目。希望通过这个项目,让大家能够快速掌握redux的使用以及它的生态,这样在以后的技术选择上会有更大的空间。您将了解redux的工作机制和基本概念。redux的使用模式。redux相关生态(react-redux、keymirror、reduce-reducers)的使用。异步action解决方案redux-thunk。项目技术选择和架构。基于react实现一个好用的任务管理平台。如何实现自己的js工具库。正文1.redux的工作机制和基本概念以上是作者画的草图,描述了redux的数据流机制。首先,用户触发一个动作(只有dispatch才能在代码层面触发一个动作)。这个时候store会自动调用reducer函数,传入之前state的state和action。reducer函数将返回一个新状态。这个时候store会监听state的变化,调用监听函数,然后我们的react组件会重新渲染,生成新的view。redux设计思想的核心是把web应用当作一个状态机,视图和状态一一对应,所有的状态都存储在一个对象中。从上图可以看出,redux的几个核心API都是store中存储数据的容器。该州某个时刻商店的快照。action标识当前要执行的操作。动作是改变状态的唯一方法。调度是执行操作的唯一方法。reducer计算和生成新状态的方式。只要弄清楚它们的关系和工作机制,redux就可以轻松上手。2、redux的使用方式熟悉了redux的基本工作流程之后,我们来看看如何在项目中使用redux。下面是使用redux的基本步骤,大家可以参考:定义初始化状态。定义动作。编写reducer函数。使用dispatch触发动作。基本代码如下://1.定义初始化状态constinitSate={num:0}//2.定义actionfunctionadd(){return{type:'INCREMENT'}}functiondec(){return{type:'DECREMENT'}}//3.编写reducer函数constreducer=(state=initState,action)=>{switch(action.type){case'INCREMENT':return{...state,{num:state.num+1}}case'DECREMENT':return{...state,{num:state.num-1}}默认值:返回状态;}}//创建storeconststore=createStore(reducer)//4.使用dispatch触发actionconstrenderView=()=>{ReactDOM.render(store.dispatch(add())}dec={()=>store.dispatch(dec())}/>,document.getElementById('root'));};renderView();store.subscribe(renderView);通过以上步骤,我们基本可以开始redux的开发了,redux也提供了中间件机制,暴露了applyMiddleware、compose等API,这里简单提一下,后面会涉及到相关的使用。在实际项目中,我们往往不会直接使用redux。我们将react-redux和其他库一起使用,通过以更优雅的方式结合react和redux来开发更易于维护的项目。3、redux相关生态(react-redux、keymirror、reduce-reducers)的使用(1)react-redux的核心思想react-redux是将所有组件分为渲染组件(纯组件)和容器组件(负责处理业务逻辑和状态),渲染组件只负责显示,不负责状态,容器组件负责处理各种状态和逻辑。所以用户只需要提供渲染组件来渲染视图,容器组件会由react-redux自动生成。所以整个过程看起来是这样的:让我们看看如何使用react-redux。首先,它提供了connect方法将UI组件生成容器组件,将UI组件和容器组件连接在一起。具体用法如下:import{connect}from'react-redux'constHomeContainer=connect(mapStateToProps,mapDispatchToProps)(Home)home是我们的UI组件。通过mapStateToProps和mapDispatchToProps这两个函数参数,我们可以将redux的store和action映射到UI组件的props上,从而实现数据正常的单向流动。mapStateToProps的作用是建立(外部)状态对象到(UI组件)props对象的映射关系,我们一般可以这样定义:constmapStateToProps=(state)=>{let{capacity}=statereturn{capacity返回的容量是我们想要传递给UI组件的props中的一个属性。mapDispatchToProps用于建立UI组件参数到store.dispatch方法的映射,我们可以这样定义:constmapDispatchToProps=dispatch=>{return{createTodo(data,cb){dispatch(createTodo(data,cb))},editTodo(data,cb){dispatch(editTodo(data,cb))}}}然后用户可以获取props中的createTodo和editTodo方法来触发store的相应动作。当然,如果我们仅仅使用上述方法,我们仍然无法将状态传递给容器组件。我们需要react-redux提供的Provider组件,它可以让容器组件获取状态。import{Provider}from'react-redux'//...render(,document.getElementById('root'))所以完整的用法应该是这样的this://Container.jsximportReactfrom'react'import{connect}from'react-redux'import{createTodo,editTodo}from'store/actions'constmapStateToProps=(state)=>{let{capacity}=state返回{capacity}}constmapDispatchToProps=dispatch=>{return{createTodo(data,cb){dispatch(createTodo(data,cb))},editTodo(data,cb){dispatch(editTodo(data,cb))}}}classHomeextendsReact.Component{//...}exportdefaultconnect(mapStateToProps,mapDispatchToProps)(Home)//index.jsimportContainerfrom'./Container'import{Provider}from'react-redux'//...render(,document.getElementById('root'))(2)keymirrorkeymirror库不是必须的,主要用来生成key==值结构,比如我们定义了一堆action类型:constactionType={A:null,B:null,C:null}//=>keymirrorconsole.log(keymirror(actionType))//==>{A:'A',B:'B',C:'C'}通过keymirror,我们可以生成key==value这样的结构。可能有人会说,这多此一举?其实可以帮助我们优化代码。Google的ClosureCompiler有一种编译模式叫高级处理(Advanced),会把K压缩成Map的格式。比如下面的代码:constType={ASVANCED:null}//经过编译,constType={a:null}会让我们的代码更小,执行更快。接下来我会继续介绍actionType的定义。(3)reduce-reducersreduce-reducers主要用于拆分reducer。想一想,如果我们的项目变得庞大而复杂,需要处理的状态非常多,那么我们写在一个reducer中是非常不优雅的,也不利于维护,如下代码所示:constreducer=(state,action)=>{switch(action.type){caseactionType.CHECK_FAIL_TODO://...caseactionType.CHECK://...caseactionType.GET_FAIL_TODO://...caseactionType.GET_NUM://。..caseactionType.COMPUTE_MONEY://...//...默认:返回状态;}}我们针对不同的业务场景一起写reducer,后期往往很难管理和维护。我们期望结合不同的业务场景,将下面的reducer分为具体的reducer,如下://reducerA.jsconstreducerA=(state,action)=>{switch(action.type){caseactionType.CHECK_FAIL_TODO://...case动作类型。检查://...默认:返回状态;}}//reducerB.jsconstreducerB=(state,action)=>{switch(action.type){caseactionType.CHECK_FAIL_TODO://...caseactionType.CHECK://...default:returnstate;}}//reducerC.js//...//合并reducer(reducerA,reducerB,reducerC)这种方式往往更利于管理和维护,我们使用reduce-Reducers可以很好的实现这一点。具体用法如下:importcreateTodoReducerfrom'./createTodoReducer'importeditTodoReducerfrom'./editTodoReducer'importdoneTodoReducerfrom'./doneTodoReducer'importdelTodoReducerfrom'./delTodoReducer'importcheckFailTodoReducerfrom'./checkFailTodoReducer'//合并多个reducer并合并状态importreduceReducersfrom'reduce-reducers'constinitialState={capacity:5*1024*1024,//localStorage总容量,单位btcurCapacity:0,//当前已用容量,单位bthasDoneTodos:[],//完成todohasFailTodos:[],//失败todounDoneTodos:[],//未完成todo//todo创建ctLoading:false,ctErrorMes:''}constreducer=reduceReducers((state=initialState,action)=>createTodoReducer(state,action),(state=initialState,action)=>editTodoReducer(state,action),(state=initialState,action)=>doneTodoReducer(state,action),(state=initialState,action)=>delTodoReducer(state,action),(state=initialState,action)=>checkFailTodoReducer(state,action));exportdefaultreducer4.异步action解决redux-thunk在了解异步action之前,先说说redux的中间件机制与koa的中间件类似,redux也支持中间件,并提供了使用中间件的API。其实原理就是重写action的dispatch流程,也就是rewritedispatch。关于中间件怎么写,这里就不详细介绍了。我们主要讲一下如何使用redux的中间件机制。redux提供的applyMiddleware和createStoreAPI是我们使用中间件的关键。我们在使用中间件的时候,需要把中间件传入applyMiddleware函数,把applyMiddleware作为createStore的最后一个参数。具体用法如下:conststore=createStore(reducer,initial_state,applyMiddleware(thunk));值得注意的是,中间件要注意中间件的使用顺序。中间件一定要按照官方的规则和具体业务的顺序排列。接下来我们看看异步操作。使用异步动作的基本模式如下:当异步开始时,当成功时,当失败时,将调度一个动作来通知用户操作的状态。我们可以想到的是在请求得到结果后派发成功/失败的动作。以上步骤有两种实现方式:在业务代码中的请求回调中触发一个同步动作。使用异步action对于简单的应用,我们可以使用第一种方式来做,所以异步action就没必要了,但是每次异步请求都手动调用两个action太粗暴了,所以最好是温柔对待项目最好的方法是使用异步操作。异步action本质上是返回一个函数,并在函数中进行相关操作,而普通的action返回的是一个对象,那么怎么处理呢?想想我们上面介绍的redux中间件机制。我们可以重写调度。确实,redux-thunk的源代码处理了dispatch并返回了一个高级函数。具体源码就不细看了。redux-thunk的源码非常简单,十几行代码,值得借鉴。以下是如何使用redux-thunk:import{createStore,applyMiddleware}from'redux';从'redux-thunk'导入thunk;从'./reducers'导入减速器;conststore=createStore(reducer,applyMiddleware(thunk))这样我们就可以愉快的写异步动作了。异步动作的伪代码如下:exportdefault(data,cb)=>{return(dispatch,getState)=>{dispatch(Actions.start());delay(0.5).then(()=>{dispatch(Actions.ok(data,cb));}).catch(error=>dispatch(Actions.fail(error||'创建失败',cb)))}}5、项目技术选型和架构前面介绍的很多东西都是redux的核心和生态使用方式,也是开发项目的基础。接下来,我们将通过一个实际项目,带你彻底掌握redux开发。我们的任务管理平台采用SPA模式开发,脚手架采用作者搭建的webpack,代码兼容ie9。目录结构如下:在实际项目开发中,也可以根据自己团队的风格和项目自身项目结构的大小进行自定义。Store是存放我们redux工作流的地方,也是整个state的管理中心。UI库作者使用了自己开发的XUI组件库,目前已经迭代了5个版本,未来会持续优化。具体参考地址如下:xui-基于react的轻量级UI组件库。6.基于react实现一个好用的任务管理平台。实现后的截图如下:通过这个任务管理平台,我们可以实现:创建任务、编辑修改任务、删除任务、任务到期自动提醒?任务效率分析、任务记录空间占用分析、创建任务:操作任务:任务效率分析、任务记录、空间占用分析。项目体验地址:XOA任务管理平台。根据上面总结的redux知识点,我们已经可以开发出上面的任务管理平台了。下一篇文章将介绍如何实现这样一个平台,开发注意事项和部署相关知识。有兴趣的朋友可以查看demo研究一下。本文转载自微信公众号“趣话前端”,可通过以下二维码关注。转载本文请联系FunTalk前端公众号。