前端开发是离不开HTTP请求的,在uni-app中,是如何发送HTTP请求的呢?
官方其实提供了一个 uni.request(OBJECT) 方法,然而,uni.request 的问题太大了,给我的感觉还像是jq时代去发送HTTP请求的用法,比如:
- 代码冗余:每次发起请求时都需要编写类似的配置代码,导致代码重复。
- 缺乏统一管理:没有统一的地方管理请求参数、头信息、错误处理等,使得代码不易维护。
像我们现在经常用的axios,提供了请求拦截器和响应拦截器,而且是 Promise 的调用方式,这让我们用起来非常舒服,那怎么把 uni.request 封装成这种方式呢?
/**
* 通用接口请求
*/
import { getToken } from '@/utils/token.js'
// 定义返回状态码,这个应该单独写一个文件
const code = {
codeOK: '0',
codeNoMobile: '-122',
codeUnLogin: '10086',
codeBan: '10087',
}
// 定义网络请求公共地址
const network = {
baseUrl: 'https://api.com'
}
// 定义公共请求参数
const defaultConfig = {
timeout: 5000,
dataType: "json",
header: {
"content-type": "application/json",
'Authorization': getToken()
}
}
const request = (options = {}) => {
return new Promise((resolve, reject) => {
// 发送请求
uni.request({
...defaultConfig,
...options,
url: network.baseUrl + options.url || '',
header: {
...defaultConfig.header,
...options.header
}
}).then(resp => {
if (resp.data.code == code.codeUnLogin) {
uni.navigateTo({
url: '/pages/login/index'
}, false)
} else if (resp.data.code == code.codeBan) {
uni.navigateTo({
url: '/pages/index/ban?banReason=' + resp.data.msg
})
}
resolve(resp.data);
}).catch(error => {
reject(error);
})
})
}
export default request
这样我们就对uni.request进行了Promise封装,这里主要是提供一个思路,具体的你还要根据自己的情况也业务进行封装,看一下使用方法:
import request from '@/utils/request.js'
export function getAddressListApi(data) {
return request({
url: '/union/productAddr/findAll',
method: 'get',
data
})
}
这样使用是不是舒服多了,有的人在这里可能会有疑问,既然已经封装了request,为什么还要把每个请求再封装一次,这就涉及到代码封装的思想问题了,同样的代码不能出现两遍。比如你在连个地方调用了这个接口,有一天后端接口地址改了,你怎么做,是不是还得全局搜索,是不是出错的概率就高了,这样封装你只需要改这一个地方。
同样的逻辑,比如返回的状态码codeOK,这种都应该放在一个统一的地方进行管理,虽然说大概率没人去改这个,但是万一呢?全局去搜索吗,搜索0可不好搜。
从代码的封装性来说就不能这么干,否则你总会有吃亏那一天的,我刚开始写代码的时候也不会这样写,吃过亏才会长教训。
封装起来有两个明显的好处:
- 高度可改性:这个状态码的值你想怎么改就怎么改,对我的业务逻辑都没有影响,而且我改起来非常快,同样是搬砖的有时候差距就在这,你觉得我要改半天,我其实1s就改好了;
- 提高可读性:如果你是一个新接手这个代码的人,res.code == '0' 和 res.code == codeOK,自己体会一下,谁能看懂等于'0'是什么意思;
还有一种封装思路,就是使用 uni.addInterceptor 这个官方提高的拦截器对request方法进行拦截,这样可以做到添加token的处理公共返回结果,但是我觉得还是用Promise的方式使用更舒服,见仁见智吧,都可以。