前言
最近在学习vue3和typescript,在用到axios的时候遇到了一点问题。之前vue2项目中是简单封装过axios,配置和使用比直接用会方便一些。到了vue3+ts项目中,我也直接将封装文件拿来用,也确实能用。但是axios返回的值的类型与我预期的大不相同,导致我想获取接口返回的数据就会报ts错误,错误提示返回值上不存在"xxx"属性。这是因为返回值类型没有定义,其实这个类型错误并不影响js运行,一样可以拿到接口返回值并使用,但是我不能接受。查找了很多文章,有些可能是代码没贴全导致报了更多的错,有些则是封装的过于抽象无法理解导致我望而却步,最后我还是找到了我需要的。
代码
首先感谢大佬的文章,原文链接,提供的代码确实是开箱即用,我这里根据自己的业务做了点微调,axios.ts代码:
// axios.ts
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
import { Dialog } from 'vant'
// 数据返回的接口
// 定义请求响应参数,不含data
interface Result {
code: number;
msg: string
}
// 请求响应参数,包含data
interface ResultData<T = any> extends Result {
data?: T;
}
const URL: string = '你的项目url'
enum RequestEnums {
TIMEOUT = 20000,
SUCCESS = 200, // 请求成功
}
const config = {
// 默认地址
baseURL: URL,
// 设置超时时间
timeout: RequestEnums.TIMEOUT,
// 跨域时候允许携带凭证
// withCredentials: true
}
class RequestHttp {
// 定义成员变量并指定类型
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config);
/**
* 请求拦截器
* 客户端发送请求 -> [请求拦截器] -> 服务器
* token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// const token = localStorage.getItem('token') || '';
return {
...config,
// headers: {
// 'x-access-token': token, // 请求头中携带token信息
// }
}
},
(error: AxiosError) => {
// 请求报错
Promise.reject(error)
}
)
/**
* 响应拦截器
* 服务器返回信息 -> [响应拦截器] -> 客户端JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response
// 全局错误信息拦截
if (data.code && data.code !== RequestEnums.SUCCESS) {
Dialog.alert({
title: '提示',
message: data.msg || '请求出错'
})
// return Promise.reject(data)
}
return data;
},
(error: AxiosError) => {
const { response } = error;
if (response && response.status !== 200) {
Dialog.alert({
title: '提示',
message: '请求失败'
})
}
}
)
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params });
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params });
}
}
// 导出一个实例对象
export default new RequestHttp(config);
根目录新建types.ts
// types.ts
// 预约
export interface Booking {
id: number
name: string
phone: string
posttime: string
}
// 预约请求体
export interface BookingReqForm {
name: string
phone: string
posttime: string
}
// 预约响应体
export interface BookingResData {
id: number
}
apis新建index.ts:
// index.ts
import axios from '@/utils/axios'
import { Booking, BookingReqForm } from '@/types'
// 获取预约列表
export const getBookingList = () => {
return axios.get<Array<Booking>>('/api/getBookingList')
}
// 新增预约
export const addBooking = (data: BookingReqForm) => {
return axios.post<BookingResData>('/api/addBooking', data)
}
组件中使用
// BookingList.vue
<script setup lang="ts">
import { Ref, ref } from 'vue';
import { getBookingList } from '@/apis/index'
import { Booking } from '@/types'
// 如果使用reactive,将data赋值给bookingList会失去响应性
const bookingList: Ref<Booking[]> = ref([])
const _getBookingList = async () => {
const { data } = await getBookingList()
if (data) bookingList.value = data
}
_getBookingList()
</script>
// AddBooking.vue
<script setup lang="ts">
import { Ref, ref } from 'vue';
import { useRouter } from 'vue-router'
const name = ref('')
const phone = ref('')
const posttime = ref('')
const router = useRouter()
const onSubmit = async () => {
const params = {
name: name.value,
phone: phone.value,
posttime: posttime.value
}
const { msg } = await addBooking(params)
Dialog.alert({
title: '提示',
message: msg || '提交成功',
}).then(() => {
// on close
router.back()
})
}
</script>
这样ts就不会报属性不存在的错误了