对于请求的封装,主要有以下目的:
其中第三点尤其重要,原因在于:
综上目的,这里选择使用PreQuest这个强大的请求库。
PreQuest 是一套 JS 运行时的 HTTP 解决方案,它包含了一些针对不同 JS 运行平台的封装的请求库,并为这些请求库提供了一致的中间件、拦截器、全局配置等功能的体验,还针对诸如 Token 的添加,失效处理,无感知更新、接口缓存、错误重试等常见业务场景,提供了解决方案。可以点击官方文档先做了解
该请求库针对不同的平台提供了不同的安装包,这里安装两个依赖包:
npm i @prequest/miniprogram @prequest/lock -S
在src->utils目录下新建requst.ts文件
import { PreQuest, create } from '@prequest/miniprogram'
import Lock from '@prequest/lock'
import { MiddlewareCallback } from '@prequest/types'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore() // 这里将token放在pinia user模块中
declare module '@prequest/types' {
interface PQRequest {
skipTokenCheck?: boolean
}
}
// 全局配置
PreQuest.defaults.baseURL = '请求域名'
// 设置header
PreQuest.defaults.header = {}
const prequest = create(uni.request)
// 无痕刷新中间件
const lock = new Lock({
getValue() {
return Promise.resolve(userStore.token)
},
setValue(token) {
userStore.token = token
},
clearValue() {
userStore.token = ''
},
})
const wrapper = Lock.createLockWrapper(lock)
const refreshToken: MiddlewareCallback = async (ctx, next) => {
if (ctx.request.skipTokenCheck) return next()
const token = await wrapper(
() =>
new Promise((resolve) => {
uni.login({
async success(res) {
if (res.code) {
// 登录获取token接口
prequest('/login', {
method: 'post',
skipTokenCheck: true,
data: { code: res.code },
}).then((res1) => resolve(res1.data.data.token)) // 注意这里根据后台返回的token结构取值
}
},
})
}),
)
if (ctx.request.header) {
// header中统一设置token
ctx.request.header['Authorization'] = `Bearer ${token}`
}
await next()
}
// 解析响应
const parse: MiddlewareCallback = async (ctx, next) => {
await next()
// 这里抛出异常,会被错误重试中间件捕获
const { statusCode } = ctx.response
if (![200, 301, 302].includes(statusCode)) {
// 在这里可以设置toast提示
throw new Error(`${statusCode}`)
}
}
// 实例中间件
prequest.use(refreshToken).use(parse)
export default prequest
首先,我们在src目录下新建api文件夹,专门存放管理请求接口。
用来存放复用的数据结构,例如请求成功返回的数据结构
// 假设接口响应通过格式
export interface ApiResp {
code: number
message: string
data: any
meta?: {
pageSize: number
total: number
current: number
}
}
接着按照功能模块进行管理,比如我们有用户相关的接口集合,在api下新建user.ts和user.model.ts两个文件,.model文件用于定义接口interface,这里值得注意的是一个接口对应两个interface,分别定义请求参数及返回的数据结构,这里可以约定统一命名格式为:参数为“Parm”后缀,返回数据为“Resp”后缀,如下示例:
import { ApiResp } from './types'
export interface GetUserListParm {
position: number
}
export interface GetUserListResp extends ApiResp {
data: GetListData[]
}
export interface GetUserListData {
name: string
position: number
}
import * as UserModel from './user.model'
import prerequest from '@/utils/request'
class UserService {
// 获取列表
static getList(params: UserModel.GetListParm) {
return prerequest.post<UserModel.GetListResp>(
'/list',
{ params },
)
}
}
export default UserService
上面文件定义了一个叫做getList的请求方法,GetUserListParm和GetUserListResp分别定义该请求的参数及返回数据结构
<script setup lang="ts">
import UserService from '@/api/user'
async function getData() {
const params = {
position: 1,
}
const res = await UserService.getList(params)
const { code, data } = res.data
if (code === 0) {
console.log(data) // 这里访问data会有类型提示
}
}
getData()
</script>
至此,我们已经完成了基本网络请求的封装和使用。而除了普通的网络数据请求,我们还可能遇到上传下载文件的需求,这里我们也一并做统一封装处理。
安装依赖拓展包
npm i @prequest/miniprogram-addon -S
通常来说,后台提供的上传接口都是公共的,我们可以在api目录下新建个common.ts文件,里面存放一些公共请求方法,例如上传下载
在type.ts中新增内容
// 文件上传成功返回数据
export interface UploadResp {
code: number
msg: string
data: {
filename: string
fileUrl: string
}
}
修改utils->request.ts,增加createUpload和createDownload的参数声明:
declare module '@prequest/types' {
interface PQRequest {
name?: string
url?: string
filePath?: string
formData?: Common
skipTokenCheck?: boolean
}
}
common.ts:
import { createUpload, createDownload } from '@prequest/miniprogram-addon'
import { UploadResp } from './types'
class CommonService {
// 上传文件
static uploadFile(filePath: string) {
const upload = createUpload(uni.uploadFile, {
name: 'imgFile',
filePath,
formData: { fileName: 'testName' },
})
return upload<UploadResp>('/fileUpload/imgUpload')
}
// 下载文件
static downloadFile(url: string) {
const download = createDownload(uni.downloadFile, {
url,
})
return download(url)
}
}
export default CommonService
页面中使用示例:
import CommonService from '@/api/common'
// 选择照片或视频
function chooseMedia(mediaType: 'image' | 'video' = 'image') {
uni.chooseMedia({
count: 1,
mediaType: [mediaType],
sizeType: ['compressed'],
maxDuration: 60,
success(res) {
const path = res.tempFiles.map((item) => item.tempFilePath)
uploadFile(path[0])
},
fail() {
// $toast("选取图片失败");
},
})
}
async function uploadFile(path: string) {
const res = await CommonService.uploadFile(path)
const { code, data } = res.data
if (code === 0) {
// 上传成功
}
}
以上内容完成了对数据请求交互及文件上传下载的封装使用,除了这些,该请求库还提供了其他一些请求处理中间件,例如请求缓存,请求错误重试,具体使用大家移步官方文档查阅即可。
值得一提的是,很多时候,我们一加载页面需要并发请求多个接口,很多人习惯直接这么写:
onload() {
getData1()
getData2()
getData3()
}
这时候我们可以借助Promise 的两个api进行优化:
阅读量:2012
点赞量:0
收藏量:0