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

我们需要什么样的服务

时间:2023-03-28 17:49:43 HTML

14世纪,英国逻辑学家奥卡姆在他的《箴言书注》中说,“不要浪费太多,做可以少花钱做好的事”。后来这句话被简化为“奥卡姆剃刀原则”,即:如果没有必要,就不要增加实体。奥卡姆剃刀适用于各个领域。它不是公理,也没有严格的推导过程,但却是一种经过实践证明的非常有效的解题方法。在编程的世界里,有太多的事情我们认为是理所当然的。我相信存在是合理的。同时,我也相信存在是有前提的,而前提会随着时间的推移而改变甚至消失。接下来想和大家一起探讨一下我们前端项目中应该剃掉的东西。前端项目中的服务层在前端项目中一般包括以下文件目录:容器:页面组件:组件utils:工具方法路由:路由服务:数据服务index.js入口文件我们的业务代码基本在containerscomponents,utils和routes也是必不可少的,但是仔细想想,我们会发现这里有一个services文件夹,叫做数据服务层,就是我们跟后端打交道的。真的需要这一层吗?让我们看看每个人如何使用该服务。//服务文件夹下的accoutService.jsimport{post}from'@/utils/request';//获取账户列表exportconstgetAccountsList=params=>post('/api/accounts.json',params);//添加accountexportconstinsertAccount=params=>post('/api/insertAccount.json',params);//更新状态exportconstupdateAccount=params=>post('/api/updateAccount.json',params);//检查账户查询exportconstcheckAccount=params=>post('/api/checkAccount.json',params);------使用----------从'@/services/accountService'导入{getAccountsList}constApp=()=>{const[name,setName]=useState('');useEffect(()=>{getAccountsList().then((res)=>{setName(res.name);});},[])return

{name}
;};从上面的代码我们可以看出services文件基本上就是一些模板代码,偶尔会有一些不常见的数据转换。这些内容与我们的业务代码无关。真的有必要写这些模板控制代码吗?服务包括什么?数据转换逻辑converHandler数据请求工具请求请求地址定义url全局拦截器拦截器附加函数openApi数据转换逻辑converHandler:不通用,有些请求在不同的页面需要不同的转换逻辑,这些转换逻辑一般写在调用位置的代码中,我也推荐这样做,因为数据转换也是某个容器的功能,而且为了测试方便,建议加入handler.js提取转换逻辑。数据请求工具request:主要封装各种请求,这部分需要统一。与业务无关的,你可以提出来。请求地址定义url:这部分业务相关性强,不应该放在服务中,而是作为服务的配置,由外部输入。globalinterceptor拦截器:处理一些常见的业务状态码,比如10001代表编辑成功。这部分也是业务相关性强,比较复杂,但是可以通过配置schema来描述,后面会讲到。附加功能openAPI:如果你系统的接口要被其他系统复用,比如MTEE基础平台的接口需要复用给运营平台,那么前端需要提供领域素材,并请求将资料在域内发送,并且必须解决跨域请求、登录、授权等问题,openAPI应运而生。综上所述,可以看出service层只需要一些统一的逻辑处理和配置文件就可以描述清楚,我们甚至可以将Service层简化为$$service=request+config$$我的服务包是这样的,我希望能够设计出这样的服务包,需要包含以下功能:请求支持常见的getpostjsonp请求,以及这些请求的附加方法,比如debounce、throttle、caching、loading等功能。你也可以提供你最喜欢的hooksAPI。接口配置一个接口包括域名domain、地址路径path、请求方法method、参数params、一些常用功能的开关,比如开启debounce{debounce:true}。在参数配置中,可以添加参数的基本属性,比如是否需要{require:true},这样就可以在包中对参数做必要的校验,从而可以将非法数据传递给背景。环境切换环境切换是一个非业务相关的功能,不应该硬编码到代码中带上线。应该只是一个配置,尽量和代码分离,所以用浏览器插件来切换是个好办法。服务包可以设计为接收域映射。这个domainMap来自window.GlobalConfig下的一个变量。浏览器插件可以动态改变这个变量来切换环境。网关转发我们写代码追求复用,从代码块的复用到组件的复用,再到业务能力的复用,而业务能力复用的载体就是领域素材。一个领域材料中有很多接口请求。如果我们把业务代码中原有的组件作为领域材料去掉,我们就得把项目中的服务层也封装起来,这样我们才能发送请求和处理一些统一的异常。我上面说的是把服务层做成一个包。别人使用的时候,只需要传入配置即可,这也是基于领域素材的场景。之后,我们还要解决另一个问题:不同站点使用域素材导致的界面跨域问题。我们目前的方案是在前端搭建一个基于节点的网关,用于接口的转发和鉴权。这个过程会集成在服务包中,外部用户只需要配置是否启用网关即可。他根本不需要知道网关是怎么转发的,就像在自己的站点下写组件一样。接口文档我们在接手其他项目的时候,总是很难找到他的接口文档,因为文档和代码是分离的,文档的维护也很滞后,甚至连文档的链接都慢慢找不到。因此,代码和文档应该放在一起,最好是代码作为文档。你可能觉得用注释就够了,但是程序员总是要求别人写注释,自己却不爱写。如果写注释能像写代码一样,或许可以规范这部分行为。例如:{name:'获取账号',domain:DOMAIN.TAOBAO,url:'/api/getAccount.json',method:METHOD.GET,params:{userId:{name:'Policypackageid',type:PARAM_TYPE.STRING,required:true,},},response:{name:'账户名'},},这里文档的格式是通过配置文件的方式标准化的,也可以结合浏览器插件查看当前使用的接口文件。异常拦截异常分为服务器异常和业务异常。服务器异常一般用http状态码、400、500等表示;业务异常需要在正文中用代码表示。在实际业务实践中,我们发现我们写一个通用的拦截器来对服务端异常做一些处理很容易,但是对于业务异常,就比较复杂了。有几个问题:很多后端不习惯使用代码返回对应的业务代码来表示不同的状态。前端直接使用后端返回的消息显示给用户。这里有两个问题。①后端需要引入第三方库来实现消息的国际化。②后台定义的消息不是用户语言,用户一般无法理解。所以这里需要第三方系统的参与。它提供了业务代码和前端动作之间的映射关系表。比如后台返回code:10000,前台应该弹出一个窗口显示消息。定义的json如下:{code:10000,message:'Editingfailed',debug:'后端数据库读写异常,堆栈信息:',showType:'openDialog'}这里的message可以返回不同的语言和文本根据不同的语言环境,showType表示前端的action类型,这个是可枚举的,其中必须有一个action,没有action,直接透传。这个第三方系统可以配置不同代码的动作,有利于异常的细粒度管理,更好的用户体验。落地实践是检验真理的唯一标准。基于以上理想,我的服务包也成型了,使用起来非常简单。只需要两步:①配置文件②导入包③在业务代码中调用配置//配置文件account.jsimport{METHOD,PARAM_TYPE}from'@ali/hulu-service';exportconstDOMAIN={TAOBAO:'//taobao.com',ALIPAY:'//alipay.com',};exportdefault{getAccount:{name:'获取帐号',domain:DOMAIN.TAOBAO,url:'/api/getAccount.json',方法:方法。GET,params:{userId:{name:'policypackageid',type:PARAM_TYPE.STRING,required:true,},},response:{name:'账户名'},},};importpackageimportHServicefrom'@ali/hulu-service';importaccountfrom'./account';//初始化serviceconstservice=HService.init({urls:[account,]});exportdefaultservice;从'./service'调用APIimport服务;constApp=()=>{const[name,setName]=useState('');useEffect(()=>{Service.getAccount().then((res)=>{setName(res.name);});},[]);return
{name}
;};exportdefaultApp;同时,基于浏览器插件,可以快速切换环境、查看接口文档等。想想边界的开始,我们讲了奥卡姆剃刀,如果没有必要,就不要增加实体,这样做的前提是有清晰独立的实体,如果我们的实体是相互连接的,那怎么办剃掉不必要的东西呢。其实无论做什么样的软件架构,都必须分清界限,即一个模块的定位是什么,哪些功能该做,哪些不该做。一个很重要的依据就是是否容易改变。哪些是业务且经常变化,哪些是非业务且通常不变。我们的代码不好,往往是因为边界不清晰,或者边界原则不一致。工程代码与业务耦合,业务代码与工程(如环境判断)混合。代码的臭味是一点一滴积累起来的,不好的开始就是最初的架构设计边界不清晰,没有用代码来定义规范。抵制代码腐败任重而道远,没有灵丹妙药,但确实可以提高一个人的系统思维。作者:ES2049/Blackstone文章可随意转载,但请保留原文链接。如果你有热情,非常欢迎你加入ES2049Studio。请将简历发送至caijun.hcj@alibaba-inc.com。