前端业务开发
前端业务开发的通用经验(二)
接口复用导致的问题
有三个层面的复用,会面临不同的问题,分别是:跨项目复用、跨页面复用、跨端复用。
1. 跨项目复用
通常是因为新开项目需要紧急上线,为了求快,直接在新项目里复用老接口,说是后面要拆,但大概率没时间拆,后续新项目的需求迭代会前赴后继的加入老接口里。等到因维护、交接、组织变动、项目拆分等原因导致接口必须独立和迁移的时候,必将付出成倍的成本。
复用的基本前提是【同步】,只要不同步,复用就一定会变成多套逻辑的杂糅。既然已经划分项目了,那么业务逻辑大概率是不同步的,如果同步,应该考虑抽成公共服务。偏离了正确的设计,早先欠的债/偷的懒最终一定会加倍偿还。
2. 跨页面复用
通常是某些公共配置,比如每个页面都需要的前置信息,因为存在重叠,就全部融合到一个公共接口。这将导致某一个页面如果要单独增加配置信息,就不得不继续加到公共接口里,越到后面,公共接口的逻辑越膨胀,性能越差,所有页面都受影响。而且公共逻辑的维护成本和变动风险也越来越大,一是历史逻辑越堆越多,就算看不懂也不敢改;二是一旦出错,就是所有页面全挂。
接口设计上,复用的层级应该降到微服务层,然后单独为每个页面提供各自的接口。
3. 跨端复用
接口一旦有 native 端使用,就应该视为“被封印”的接口,因为通常后续开发只能加字段,不能删字段,也不能改字段(除非接口作版本控制)。一定程度上这给前端带来了某种好处,因为很多时候前端可以凭此特性,强势要求把逻辑挪到后端:“因为咱 native 有版本问题,不能改 😁”。哈哈,其实更合理的理由是:跨端复用。
接口格式不统一的问题
这估计也是个行业普遍的老大难问题,有个老兄在微信四面还是五面的时候就被问到过。前端作为聚合各类资源的生产链终端,只要对接超过 1 个后端团队,大概率会遇到这个问题。具体表现有:
- 数据格式不一致。主要是状态码和异常信息的定义不一致,比如状态码,用哪个 Int 表示【正常】无法统一倒也罢了,连用 Int 表示状态码都不见得统一,竟然有字符串格式的数字
- content-type 不一致,有的 form,有的 json
- 参数位置不一致。有些 post 接口偏要在 query 里取参,有些接口遵照 restful 规范把参数定义到了 path 里。明明可以也应该从 cookie 里取的参数偏要在请求时传参
- 多种不同的异常,共用同一个状态码
面临上述问题的情况下,请问公共的网络请求方法怎么封装?
约定往往解决不了问题,因为约定根本没有约束力,而且你不见得有那个权力,有这个权力的人不见得关心这个问题。
如果按照【谁痛谁解决】的原则,前端自己写接口,肯定会增加工作量和维护成本,对人要求太高,除了大厂里少数团队外,在其它地方大概率搞不动(小公司另说,可能总共就一个后端团队,或者前后端没分那么彻底,不存在数据格式不统一的问题)。当然前端可以参与接口的定义,对接口提要求。
一种解决思路是:网络请求 request 方法,做成可配置的。比如:
const requestServiceA = new Request({
contentType: 'json',
normalStatus: 0,
commonParams,
host
})
const requestServiceB = new Request({
contentType: 'form',
normalStatus: 200,
commonParams,
host
})
还有一种思路是:在网关层做一次适配改造。
字段复用与二义性问题
假设有三个页面用到了同样的内容,复用同一个字段是否合理?是否复用同样取决是否【同步】,假设这个内容是在后台配置的,那么修改配置的人,知不知道改了这个配置,不仅会影响到页面 A,还会影响页面 B 和 C?假设页面 B 的内容要换成另一个怎么办?
以“提高效率、省事”为理由的复用,大概率是会出问题的。
另一种类型的复用,是语义上的复用,这个问题就更大了。举个例子,假设公交卡有三种类型:普通卡、学生卡、老年卡,普通卡都是余额,学生卡都是次卡,老年卡都是免费卡,那么能用【卡类型】来判断【计价模式】与【是否免费】吗?
const isFree = cardType === '老年卡'
const isPayPerUse = cardType === '学生卡'
如果你这么做了,需求一定会出来狠狠打你的脸,一定会出现非免费的老年卡、以次数计价的老年卡、以金额计价的学生卡。
每个字段应该只具备一个语义,不要用一个字段干两件不同的事情。
接口请求的防重、节流和竞态问题
如果一个按钮只是绑定了请求事件,没做任何处理,那么短时间咣咣点击,连续发送多个异步请求,很容易出问题。如果是 create 型接口,那么会导致重复创建;如果是 get 型接口,那么可能遇到竞态问题。
原则上,非幂等接口,要考虑防重;幂等接口,要考虑节流和竞态处理。