> 文章列表 > TS+Axios的封装(vue3.x环境)

TS+Axios的封装(vue3.x环境)

TS+Axios的封装(vue3.x环境)

1. 创建axios实例

# 项目src下新建utils/request.ts
# npm i axios 是必须滴
import axios from 'axios'const baseURL = 'https://ilovesh.com' // 基地址
const instance = axios.create({baseURL,timeout: 3000 // 请求超时时间 (3秒超时)
})export { baseURL, instance }

2. 添加拦截器

// 添加请求拦截器
instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config},function (error) {// 对请求错误做些什么return Promise.reject(error)}
)// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 2xx 范围内的状态都会触发该函数。// 对响应数据做点什么return response},function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error)}
)

3. 剥离数据

// 把上图圈出的数据剥离出来
// 修改响应拦截器的return值
...
function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response.data
}
...

4. 请求头携带Token

笔者首先在 App.vue 里写了个测试用例,先拿到Token:

import { instance } from '@/utils/request'
import { useUserStore } from '@/stores'// pinia仓库
const userStore = useUserStore()instance.post('/login/password', {mobile: '13230000001',password: 'abc12345'}).then((res) => {// 调用仓库里的自定义存储方法 (现在仓库里有Token了)userStore.updateUser(res.data)})

接下来修改axios实例的请求拦截器(在请求头里携带Token):

instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 获取Pinia仓库const userStore = useUserStore()const token = userStore.user?.tokenif (token) {config.headers.Authorization = `Bearer ${token}`}return config},function (error) {// 对请求错误做些什么return Promise.reject(error)}
)

配置完成后,最好能找个需要Token才能访问的接口测试一下!

5. 业务状态码出错的处理

这里指的是响应是成功的(2xx 范围内的状态码),业务是失败的。笔者这里的业务逻辑成功与否根据code区分,code = 10000 成功,code !== 10000 失败

 修改axios实例的响应拦截器:

// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数// 对响应数据做点什么const { code, message } = response.dataif (code !== 10000) return Toast(message) // 这里使用vant库的轻提示组件return response.data},function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error)}
)

6. 响应状态码401报错的处理

状态码 401 Unauthorized 代表客户端错误,指的是由于缺乏目标资源要求的身份验证凭证,发送的请求未得到满足。说人话就是“Token失效/出错”!

为了尽可能追求完美,这里的逻辑比较复杂:

① Token失效,使用路由转回登录页

② 假设在 abc 页面访问,这里发现 Token 失效了,我们使用路由转回了登录页;

        重新登录成功后,体验好的app会让我回到本来访问的abc页面,并且携带可能的路径参数;

③ 实现方式:把原本的页面路径&参数全部给到query带上,重新登录成功就能完美转回了;

        如何获取原本页面的路径&参数?使用 router.currentRoute.value.fullpath

// 响应拦截器失败的函数内修改
function (error: AxiosError) {// 超出 2xx 范围的状态码都会触发该函数// 对响应错误做点什么if (error.response?.status === 401) {// Pinia里删除用户信息const userStore = useUserStore()userStore.delUser()// 跳转登录,带上接口失效所在页面的地址,登录完成后回跳使用router.push({path: '/login',query: { redirectURL: router.currentRoute.value.fullPath }})}return Promise.reject(error)
}

7. 封装请求函数&配泛型

# 使用axios实例的伪代码
instance({method: '',url: '',params / data (get请求的时候是params,get请求以外的是data)
})# 希望封装一个函数,它的使用是 request<泛型>(url, method, 可能有/可能无的接口请求参数)

 雏形大概长这样:

import axios, { AxiosError, type Method } from 'axios'// 请求工具函数
const request = (url: string, method: Method = 'get', requestParams?: object) => {return instance.request({url,method,[method.toLowerCase() === 'get' ? 'params' : 'data']: requestParams})
}// 按需导出
export { baseURL, instance, request }

加上泛型的最终版

// 这个需要替换 axios.request 默认的响应成功后的结果类型
// 之前是:传 { name: string } 然后res是   res = { data: { name: string } }
// 但现在:在响应拦截器中返回了 res.data  也就是将来响应成功后的结果,和上面的类型一致吗?
// 所以要:request<数据类型,数据类型>() 这样才指定了 res.data 的类型
// 但是呢:后台返回的数据结构相同,所以可以抽取相同的类型
import axios, { AxiosError, type Method } from 'axios'
type Data<T> = {// 内容根据接口返回的规则做修改code: numbermessage: stringdata: T
}
// 请求工具函数
const request = <T>(url: string, method: Method = 'get', requestParams?: object) => {return instance.request<T, Data<T>>({url,method,[method.toLowerCase() === 'get' ? 'params' : 'data']: requestParams})
}// 按需导出
export { baseURL, instance, request }

使用范例:

import { request } from '@/utils/request'
import type { User } from '@/types/user'request<User>('/login/password', 'post', {mobile: '13230000001',password: 'abc12345'
}).then((res) => {// 调用仓库里的自定义存储方法userStore.updateUser(res.data)
})

End-------------------