首页多个接口并行加载与统一状态
接口“能调通”和网络层“写得稳”,其实是两回事。Demo 里只有一个接口时,直接在页面里请求最快;真实项目一旦出现登录态、公共 Header、超时、分页、并发、错误码和缓存,页面很容易被网络代码塞满。
这一篇就从首页多个接口并行加载与统一状态这个问题切进去。代码不追求花哨,重点是把调用链理顺,让后面加需求时还能看得懂、查得动。
1. 为什么接口越多,页面越容易乱
最常见的写法是点击按钮以后直接创建请求、拼 URL、组 Header、解析 JSON,然后顺手改 loading。第一版确实很快。
问题是第二个页面会复制一遍,第三个页面再复制一遍。很快就会出现超时时间不一样、错误码判断不一样、Token 有的加有的没加。真正难维护的不是 HTTP,而是这些散落的规则。
所以第一步不是造一个“万能网络框架”,而是找出项目里真正重复的部分。
2. 先保留一个最小核心
async loadHome() { this.loading = true try { const [user, notices, tasks] = await Promise.all([ this.userService.getUser(), this.noticeService.getNotices(), this.taskService.getTasks() ]) this.user = user this.notices = notices this.tasks = tasks } finally { this.loading = false } }这段代码只表达本篇最重要的结构。真实工程里还会有 URL 配置、模型转换、日志和错误映射,但先别一次全塞进来。
3. 页面应该知道多少网络细节
我比较喜欢让页面只知道“我要查用户”“我要加载列表”,而不是让页面知道请求方法、响应头和 JSON 怎么解析。
页面可以保留 UI 状态:
async query() { if (this.loading) return this.loading = true try { this.data = await this.repository.load() this.pageStatus = this.data.length > 0 ? PageStatus.Success : PageStatus.Empty } catch (e) { this.pageStatus = PageStatus.Error } finally { this.loading = false } }这里页面关心的是“加载中、成功、空、失败”。至于底层为什么失败,可以交给统一的数据层去归类。
4. loading 不应该只有一个
首页三个模块同时请求时,如果大家共用一个全局 boolean,第一个接口回来就可能把 loading 关掉,剩下两个接口还在路上。
所以要先判断 loading 的粒度:整页、局部模块,还是某个提交按钮。不同粒度最好不要混用。
5.catch不是错误治理的终点
断网、超时、业务校验失败、登录失效,对用户来说完全不同。
如果最后都变成一句“请求失败”,开发方便了,用户体验却很差。更稳的做法是把底层异常映射成应用内部统一的错误类型,再由页面决定展示什么。
例如登录失效可以触发重新认证;断网显示网络状态页;业务错误直接展示后端给出的可读原因。
6.finally很适合做状态恢复
很多“查询按钮第二次点不了”“页面一直转圈”的问题,最后查下来就是异常以后没有恢复状态。
this.loading = true try { await this.service.submit() } catch (e) { this.handleError(e) } finally { this.loading = false }无论成功还是失败都必须执行的动作,放finally比在多个分支里重复写稳得多。
7. 连续点击一定要测
第一次点击正常,不代表逻辑正常。连续点三次、请求没回来就再点、失败以后再点,这些才容易暴露状态问题。
提交类接口尤其需要防重复。重复查询最多浪费资源,重复提交可能真的产生两笔业务数据。
最简单的一层保护就是请求中的状态锁,但要确保异常路径也能释放。
8. 日志别写成“信息垃圾”
日志最好带模块名和阶段:
console.info('[NetworkPage] request start') console.info('[NetworkPage] request success') console.error('[NetworkPage] request failed')Token、身份证号、手机号这类敏感信息不要完整打印。响应很大时也别整包输出,关键字段和错误码通常更有价值。
9. 弱网才是真正的压力测试
开发环境网络太顺时,很多问题看不出来。把接口延迟拉长以后,页面有没有明确反馈?用户会不会重复点击?页面已经离开后,请求回来还会不会做无意义更新?
断网恢复也值得测。不是所有场景都应该自动重试,提交类操作尤其要谨慎。
10. 缓存也不是越多越好
缓存解决的是速度和可用性,不是为了少调接口而少调接口。
用户余额、权限这类数据通常更强调新鲜度;文章列表、配置项可能允许短时间缓存。不同数据应该有不同策略。
如果缓存没有过期时间、刷新机制和来源说明,最后很容易变成“为什么页面一直是旧数据”。
11. 分层别搞得太玄学
一个实用的结构通常就够了:
Page → Repository → Service → HttpClient
Page 管 UI 和交互;Repository 决定数据从哪里来;Service 描述具体接口;HttpClient 处理协议共性。
不是每个项目都必须四层齐全。项目小就少一层,业务复杂再拆。重点是职责明确,不是目录数量。
12. 我实际会怎么排查接口没走
顺序很固定:事件有没有触发 → 方法有没有进入 → 请求前日志有没有打印 → 参数是否正确 → Promise 有没有 resolve/reject → catch 有没有提前走 → finally 有没有恢复状态。
这种沿调用链往下查的方法,比一上来清缓存、重启 DevEco Studio 靠谱得多。
13. 提交前至少测这些场景
正常返回、空数据、业务失败、断网、超时、连续点击、页面重新进入。
如果是分页,再测最后一页、重复数据和刷新后重新分页。如果涉及登录态,再测 Token 失效和重新登录。
网络代码真正的质量,往往就体现在这些“不顺利的时候”。
总结
本篇真正要建立的是一条清楚的数据链:**页面发起业务动作,数据层负责拿数据,异常统一归类,页面根据结果更新 UI 状态。**把这条链稳定下来,后面的并发、Token、分页、缓存和弱网才有合适的位置,不会继续堆回页面。从你现有项目里挑一个接口,把页面里的 URL、JSON 解析和公共错误码判断移出去。页面最后只留下“调用、状态、展示结果”。然后故意让接口失败一次,看看状态能不能正常恢复。