他来了,他来了,走路都掉头发了。大家好,我是布壮,一名前端开发者。看完作者上一篇《俺咋能看懂公司前端项目?》,不知道大家有没有学习到它的设计思想,并应用到自己的项目中。我相信你,绝对不是。趁我们头发浓密,开始正事吧!今天的主角React,作为当今社会主流的前端框架,在前端框架界算是老大哥了。Project,并衍生出很多其他的框架,比如跨平台移动开发ReactNative,它的一套代码可以运行在Android和iOS上。不过,这些都不是本文的重点。今天的重点是React或者ReactNative是如何高效管理调用后端接口的。像上篇文章中提到的Vue管理后台界面,它们有很多相同点和不同点,因为我们知道它们的开发模型和方法是有些不同的。最初的想法是Vue有自己独立的状态管理器Vuex,React也可以使用Redux来管理状态;Vue提供了混入(mixins)的开发方式,虽然React一开始也有混入的功能,但后来被废弃了,但是React的混入功能可以通过高阶组件来实现。基于这些想法,该出手的时候出手,参与北斗七星。后来,我终于学会了让自己爱自己。我犯了一个错误。我终于学会了Redux和React-redux。它其实和Vuex是一样的,只是有些概念不一样。在Vuex中,State定义状态,Mutation修改状态,Action支持异步调用Mutation修改状态,Getter从State中导出状态。在Redux中,主要有Reducer和Action。Reducer是一个纯函数,根据不同的Action返回不同的状态。行动是改变状态的唯一途径。但是Redux提供的功能只支持状态的同步修改,但是Redux有很多中间件,其中Redux-thunk就是一个支持异步的中间件,因为使用Redux来管理请求需要异步的支持,所以,我要你.Do-re-miFassolaxi又唱了起来。前面说了这么多就是凑字数了,下面开始我们的步骤。首先了解一下前端管理后台界面的架构设计流程。技术选型后端采用Swagger接口管理,前端React采用Redux状态管理,React-redux状态映射组件Props,Redux-thunk支持异步管理状态。分析Swagger需要使用Handlebars模板编译和fs文件解析。1.引入招摇。后台接口服务器引入swagger。这一步我就不多说了,你有我的都有,嘿嘿,加入北斗。2.分析swagger生成controller。可以通过js编写脚本生成指定格式的js文件。swagger提供的v2/api-docsURL可以访问接口的json。这个json是一个固定格式的字符串,包含标签数组和路径对象。以下格式的JS文件是通过Handlebars模板编译和fs文件解析生成的,每个类对应一个文件。同时生成一个index.js入口文件,集中装饰所有controller文件。exportdefault{actions:{findById:{summary:'主键查询',method:'get',url:(payload)=>`/api/user/${payload.id}`,parameters:[{'name':'id','in':'path','description':'id','required':true,'type':'string'}],},},}3.为每个控制器文件生成相应的动作和减速器功能。上述入口文件index.js用于装饰各个控制器。装饰的内容是遍历controller文件的actions对象,生成actions函数和reducers纯函数。最后将生成的reducer交给redux进行管理,action提供对组件的调用。actions函数中分为三个步骤,包括对请求前、请求中和请求后状态的处理。这三步分别是设置接口请求的加载状态,通过加载状态处理页面的加载效果,将自定义逻辑判断保存在组件中。下图展示了action函数中各个接口的数据处理。生成action和reducer的代码:exportdefault(name,controller)=>{constdefaultState=(type)=>({//设置请求前的数据状态,生成reducer时使用type:type,loading:false,error:null,request:null,data:{},})conststart_request=(type)=>({//设置开始请求的数据状态type:type,loading:true,error:null,request:null,data:{},})letactions={}letreducers={}//遍历生成的controller文件的action_.forEach(_.keys(controller.actions),key=>{constaction=controller.actions[key]//生成actionactions[`${name}_${key}`]=(payload)=>{returnasync(dispatch)=>{//设置开始请求dispatch(start_request(`${name}_${key}`))//开始网络请求letresp={}try{resp=awaitajaxUtil.myRequest(action,payload)}catch(e){resp=e}lethandleResult=null//数据处理,这里是resp的错误。status状态码为400-500Processing,200-300数据处理成功if(!resp){handleResult={type:`${name}_${key}`,loading:false,error:'sy词干错误',data:null,}}elseif(resp.status>=400&&resp.status<500){handleResult={...}}elseif(resp.status>=500){handleResult={...}}elseif(resp.status>=200&&resp.status<300){handleResult={type:`${name}_${key}`,loading:false,error:null,request:payload,data:resp.data,}}//请求结束数据状态处理dispatch(handleResult)returnhandleResult}}//生成reducerreducers[`${name}_${key}`]=(state=defaultState(`${name}_${key}`),action)=>{if(action.type===`${name}_${key}`){return{...state,...action}}else{returnstate}}})return{...actions,reducers}}4.封装高层components,将接口请求状态数据映射到组件的props。vuex中有四个辅助函数。这个react-redux即将登场。react-redux提供了一个连接,它是一个高级组件,它接收一个React组件作为输入并输出一个新的React组件。高阶组件使代码更具可重用性、逻辑性和抽象性。可以劫持渲染方法,也可以控制道具和状态。这里我们需要自己封装一个高阶组件,它调用react-redux提供的connect函数来映射state并dispatch到组件的props上。另外,我们需要定义两个函数映射到props,一个是用来调用接口的函数,另一个是获取请求接口的加载状态函数。最后返回一个新组件。高层组件封装如下:exportdefaultfunctionconnect(states=null,dispatches=null){return(WrappedComponent)=>{constmapStateToProps=state=>{//该函数用于获取网络请求的加载,是用于显示组件的加载指示constloading=(scope)=>{returnstate[`${scope.controller}_${scope.method}`].loading}returnstates?{...state,...states(状态),加载}:{。..state,loading}}//集中处理请求发送的异常consterror=(cbData)=>{//判断cbData.error,用于alert()提示用户错误信息}constmapDispatchToProps=dispatchAsync=>{//该函数用于在组件中发起网络请求(payload))error(cbData)returncbData}returndispatches?{...dispatches(dispatchAsync),dispatch}:{dispatch}}//reduxConnect是react-redux提供的,原名connect,我这里有个别名,为了避免和我封装的高层组件名称冲突//import{connectasreduxConnect}from'react-redux'constRoot=reduxConnect(mapStateToProps,mapDispatchToProps)(WrappedComponent)returnclassHOCextendsReact.Component{render(){return<根{...this.props}/>}}}}5.组件是指自定义的高阶组件,如果组件涉及到网络请求,可以引入。引入之后是这样的:exportdefaultconnect(mapStateToProps,mapDispatchToProps)(Home),其中Home是组件,mapStateToProps和mapDispatchToProps要指定哪些数据映射到props,哪些不能传。然后就可以为所欲为了,发起网络请求this.props.dispatch(IUserController.findById,{id}),返回值为请求后的数据。获取请求状态this.props.loading(IUserController.findById),返回值为true或false。上面,我重点介绍了react是如何管理调用接口的。其实reactnative的设计也是一模一样的。您不妨尝试设计一下。有很多事情只能通过不断的试错才能实现。本文就到此为止,我们下期再见。
