外观
请求库应该封装到什么程度?
基址、超时、登录态和错误提示放在一处。每个接口返回业务数据,而不是让页面自己重复读 response.data.data。不要把界面路由和提示框全部焊死在拦截器里。
原理
实例上设置 baseURL 和超时。请求拦截器附加令牌。响应拦截器区分:
- HTTP 或网络失败
- 业务码表示未登录,这时才清理会话并跳转登录
- 业务码表示失败,把消息变成拒绝原因
页面拿到的应是已经解开的数据。列表页如果要自己展示空状态,就不要在拦截器里一律弹失败框。调用方传入 silent 一类的选项,明确这次失败由页面处理。
取消用 AbortController。拦截器里如果共享一个取消源,后一次请求会取消前一次不相关的请求。按一次用户操作创建控制器。见 搜索与请求竞态。
令牌刷新要排队:第一个 401 去刷新,其余请求等着新令牌再重放。刷新接口自己不能再走「401 就刷新」,否则循环。
类型上给每个接口声明响应类型。运行时仍要检查关键字段,类型擦除后不管服务器。
追问
要不要按页面再包一层?
页面需要的是「加载中、错误、数据」。这层用组合函数或请求库的缓存,而不是再写一个只转发 axios 的类。封装到团队能说清责任为止。
上传和普通 JSON 共用实例吗?
可以共用鉴权。超时和 Content-Type 分开。上传要进度时看适配器支不支持,不支持就单独用 XMLHttpRequest。见 断点续传。
易错点
- 拦截器里直接改全局路由,单元测试和同时发出的请求很难处理。
- 所有错误都
Promise.reject之后又不在页面捕获,控制台只有未处理拒绝。 - 把后端整个响应当成前端领域模型,字段一改所有页面跟着碎。