


以前vue2当中的详细的请求封装以及跨域的可以参考:
https://www.tnblog.net/aojiancc2/article/details/4751
其实逻辑基本都是一致的,里边封装的步骤列得比较详细
先进行请求封装
在src下创建一个common文件夹里边创建一个request.js,加入封装代码。当然文件夹放哪里根据自己的需要来就行了。
加入简单封装的代码
import axios from "axios";
import qs from "qs";
// vue3下的element-plus
import { ElLoading , ElMessage } from "element-plus";
const loading = {
// loading加载对象
loadingInstance: null,
// 打开加载
open(loadtext) {
if (this.loadingInstance === null) {
// 如果实例 为空,则创建
this.loadingInstance = ElLoading.service({
text: loadtext, // '加载中...', // 加载图标下的文字
spinner: "el-icon-loading", // 加载图标
background: "rgba(0, 0, 0, 0.5)", // 背景色
customClass: "loading", // 自定义样式的类名
});
}
},
// 关闭加载
close() {
// 不为空时, 则关闭加载窗口
if (this.loadingInstance !== null) {
this.loadingInstance.close();
}
this.loadingInstance = null;
},
};
axios.defaults.timeout = 300000; // 设置超时时间
// 配置请求头数据格式
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
// 请求拦截器
// 请求拦截器
axios.interceptors.request.use(
(axiosconfig) => {
// 请求地址统一增加一个webapi,因为后台那个代理配置里边有加上webapi的前缀
//axiosconfig.url = "/webapi" + axiosconfig.url;
//从环境变量读取,线下访问接口增加webapi前缀,上线就不增加
// axiosconfig.url = process.env.VUE_APP_BASE_API + axiosconfig.url;
axiosconfig.url = "" + axiosconfig.url;
//axiosconfig.headers.Authorization = "Bearer " + token;
return axiosconfig;
},
(err) => {
loading.close(); // 关闭加载窗口
return Promise.reject(err);
}
);
// 响应拦截器
axios.interceptors.response.use(
(res) => {
//console.log("统一响应了");
loading.close(); // 关闭加载窗口
// 如果token过期了就直接跳转到登录
// if (
// (res.code === 401 || res.code === 500 || res.code === 403) &&
// !sessionStorage.getItem("401show")
// ) {
// // to re-login
// sessionStorage["401show"] = true;
// ElMessage.confirm("登录状态已过期", "登录提示", {
// confirmButtonText: "重新登录",
// cancelButtonText: "取消",
// type: "warning",
// })
// .then(() => {
// sessionStorage.clear("401show");
// // 跳转到登录页
// location.href = "/#/login?redirect=/school-dashboard#/zuxia/index";
// })
// .catch(() => {
// sessionStorage.clear("401show");
// });
// }
return res;
},
(err) => {
loading.close(); // 关闭加载窗口
return Promise.reject(err);
}
);
const request = {
get(url, data, loadtext) {
// 判断有没有参数,有参数就把参数加上
if (data) {
url = url + "?" + qs.stringify(data);
}
if (loadtext && loadtext.length > 0) {
loading.open(loadtext); // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.get(url)
.then(function (response) {
// console.log("封装的方法数据回来了");
// console.log(response)
if (response.status === 200 && response.data.code === 200) {
resolve(response.data);
} else {
// 上面的判断要根据实际情况修改判断对哦,不然使用else里边的reject输出,虽然结果也能正确输出,但是浏览器上会提示Uncaught (in promise),也不能继续正确的向下执行了。
reject(response.data);
}
})
.catch((e) => {
reject(e);
});
});
},
post(url, data, loadtext) {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext); // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.post(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === 200) {
resolve(response.data);
} else {
reject(response.data);
}
})
.catch((e) => {
reject(e);
});
});
},
};
export default request;
这个是更完善一点的封装方法
增加了put,delete等方法的封装
import axios from 'axios'
import qs from 'qs'
// vue3下的element-plus
import { ElLoading, ElMessage } from 'element-plus'
// 后台接口返回成功的状态码,需要根据自己的接口修改
const successCode = 200
const loading = {
// loading加载对象
loadingInstance: null,
// 打开加载
open(loadtext) {
if (this.loadingInstance === null) {
// 如果实例 为空,则创建
this.loadingInstance = ElLoading.service({
text: loadtext, // '加载中...', // 加载图标下的文字
spinner: 'el-icon-loading', // 加载图标
background: 'rgba(0, 0, 0, 0.5)', // 背景色
customClass: 'loading' // 自定义样式的类名
})
}
},
// 关闭加载
close() {
// 不为空时, 则关闭加载窗口
if (this.loadingInstance !== null) {
this.loadingInstance.close()
}
this.loadingInstance = null
}
}
axios.defaults.timeout = 300000 // 设置超时时间
// 配置请求头数据格式
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
// 请求拦截器
axios.interceptors.request.use(
(axiosconfig) => {
// 请求地址统一增加一个webapi,因为后台那个代理配置里边有加上webapi的前缀
//axiosconfig.url = "/webapi" + axiosconfig.url;
//从环境变量读取,线下访问接口增加webapi前缀,上线就不增加
// axiosconfig.url = process.env.VUE_APP_BASE_API + axiosconfig.url;
axiosconfig.url = '' + axiosconfig.url
// console.log("请求的地址2",axiosconfig.url)
// 测试token , 获取token的方法换成自己的写法即可
let token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkU0QUU1Rk...."
axiosconfig.headers.Authorization = "Bearer " + token;
return axiosconfig
},
(err) => {
loading.close() // 关闭加载窗口
return Promise.reject(err)
}
)
// 响应拦截器
axios.interceptors.response.use(
(res) => {
//console.log("统一响应了",res);
loading.close() // 关闭加载窗口
if(res.data.code!==successCode)
{
ElMessage({
message: res.data.msg,
type: 'error'
})
}
// 如果token过期了就直接跳转到登录
// if (
// (res.code === 401 || res.code === 500 || res.code === 403) &&
// !sessionStorage.getItem("401show")
// ) {
// // to re-login
// sessionStorage["401show"] = true;
// ElMessage.confirm("登录状态已过期", "登录提示", {
// confirmButtonText: "重新登录",
// cancelButtonText: "取消",
// type: "warning",
// })
// .then(() => {
// sessionStorage.clear("401show");
// // 跳转到登录页
// location.href = "/#/login?redirect=/school-dashboard#/zuxia/index";
// })
// .catch(() => {
// sessionStorage.clear("401show");
// });
// }
return res
},
(err) => {
loading.close() // 关闭加载窗口
console.log(err)
ElMessage({
message: err,
type: 'error'
})
return Promise.reject(err)
}
)
const request = {
get(url, data, loadtext) {
// 判断有没有参数,有参数就把参数加上
if (data) {
url = url + '?' + qs.stringify(data)
}
console.log("请求的地址",url)
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.get(url)
.then(function (response) {
// console.log("封装的方法数据回来了");
// console.log(response)
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
// 上面的判断要根据实际情况修改判断对哦,不然使用else里边的reject输出,虽然结果也能正确输出,但是浏览器上会提示Uncaught (in promise),也不能继续正确的向下执行了。
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
post(url, data, loadtext) {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.post(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
put(url, data, loadtext) {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.put(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
delete(url, data, loadtext) {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.delete(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
}
}
export default request
tip:其他的封装方法比如ts版本的请求封装可以参考下面的写法
在配置好跨域
vue3的跨域配置在vite.config.js中,配置方法和以前几乎完全一致,根据自己的需求修改即可。
直接使用封装的代码调用接口
在需要使用的组件引入封装的js:
import request from '@/common/request'
把onMounted这些也引入一下:
import { reactive, toRefs, onMounted } from 'vue'
在onMounted中调用接口
const initData = () =>{
//测试接口访问
request.get('/api/v1/Home/1',{}).then(res => {
console.log("测试数据返回")
console.log(res)
})
}
onMounted(() => {
initData()
})
数据被成功输出了:
注意要把跨域配置好,不然会报跨域相关的错误。
全局引入封装的请求js
在main.js中加入相关代码
vue2中的写法:
import request from "./common/request.js";
Vue.prototype.$http = request;
vue3不能这样写了,应该这样写:
import request from "./common/request.js";
const app = createApp(App)
// vue3中要使用下面的写法
app.config.globalProperties.$http = request
调用方法
先引入:
import { reactive, toRefs, getCurrentInstance, onMounted } from 'vue'
let internalInstance = getCurrentInstance() as any
const $http = internalInstance.appContext.config.globalProperties.$http
调用:
const initData = () => {
//测试接口访问
$http.get('/api/v1/Home/1').then(res => {
console.log("测试数据返回")
console.log(res)
})
}
onMounted(() => {
initData()
})
也可以使用异步的方式调用接口
const fetchData = async () => {
const res = await $http.get('/api/v1/Home/1')
console.log("测试数据返回了")
console.log(res);
}
onMounted(() => {
fetchData()
})
直接使用axios调用接口也是可以异步使用的
const fetchData = async () => {
// const res = await $http.get('/api/v1/Home/1')
// console.log("测试数据返回了")
// console.log(res);
const res = await axios.get('/api/v1/Home/1')
console.log("测试数据返回了.")
console.log(res.data);
}
onMounted(() => {
fetchData()
})
单独提出来api方法
其实现在调用api也可以不用做成全局的方式,更多的是使用把api请求的方法单独提出来写
首先在src文件夹下面创建好api,然后加入需要的js文件:
封装好需要调用api的方法:
import request from '@/common/request'
/**
* @method getArticle 获取文章信息
* @param {*} data
* @returns
*/
export function getArticle(data) {
return request.get('/api/v1/Home/1',data)
}
调用:
先引入封装的js:
import { getArticle } from '@/api/article'
使用异步的方式调用方法:
const fetchData = async () => {
const res = await getArticle({})
console.log("测试数据返回了..")
console.log(res);
}
onMounted(() => {
fetchData()
})
使用then的方式调用
const fetchData = () => {
getArticle({}).then(res => {
console.log("测试数据返回了...")
console.log(res)
})
}
onMounted(() => {
fetchData()
})
如果返回的数据是这种格式
取list和total可以这种取:
const fetchData = async () => {
const {
data: { list, total },
} = await getInquiryInfo({})
console.log("测试数据返回了..")
console.log(list);
console.log(total);
}
完善请求封装
把请求封装修改为ts版本
import axios from 'axios'
import qs from 'qs'
// vue3下的element-plus
import { ElLoading, ElMessage } from 'element-plus'
// 后台接口返回成功的状态码,需要根据自己的接口修改
const successCode = 0
// loading加载对象
// let loadingInstance: any
const loading = {
// loading加载对象
loadingInstance:null as any,
// 打开加载
open(loadtext: any) {
//if (this.loadingInstance === null) {
// 如果实例 为空,则创建
this.loadingInstance = ElLoading.service({
text: loadtext, // '加载中...', // 加载图标下的文字
// spinner: 'el-icon-loading', // 加载图标
background: 'rgba(0, 0, 0, 0.6)', // 背景色
customClass: 'loading' // 自定义样式的类名
})
//}
},
// 关闭加载
close() {
// 不为空时, 则关闭加载窗口
if (this.loadingInstance) {
this.loadingInstance.close()
}
// this.loadingInstance = null
}
}
axios.defaults.timeout = 300000 // 设置超时时间
// 配置请求头数据格式
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
// 请求拦截器
axios.interceptors.request.use(
(axiosconfig) => {
// 请求地址统一增加一个webapi,因为后台那个代理配置里边有加上webapi的前缀
//axiosconfig.url = "/webapi" + axiosconfig.url;
//从环境变量读取,线下访问接口增加webapi前缀,上线就不增加
// axiosconfig.url = process.env.VUE_APP_BASE_API + axiosconfig.url;
axiosconfig.url = '' + axiosconfig.url
// console.log("请求的地址2",axiosconfig.url)
// 测试token , 获取token的方法换成自己的写法即可
let token = "eyJhbGciOiJSUzI1NiIsImtpZCI"
axiosconfig.headers.Authorization = "Bearer " + token;
return axiosconfig
},
(err) => {
loading.close() // 关闭加载窗口
return Promise.reject(err)
}
)
// 响应拦截器
axios.interceptors.response.use(
(res) => {
//console.log("统一响应了",res);
loading.close() // 关闭加载窗口
if (res.data.code !== successCode) {
ElMessage({
message: res.data.msg,
type: 'error'
})
}
// 如果token过期了就直接跳转到登录
// if (
// (res.code === 401 || res.code === 500 || res.code === 403) &&
// !sessionStorage.getItem("401show")
// ) {
// // to re-login
// sessionStorage["401show"] = true;
// ElMessage.confirm("登录状态已过期", "登录提示", {
// confirmButtonText: "重新登录",
// cancelButtonText: "取消",
// type: "warning",
// })
// .then(() => {
// sessionStorage.clear("401show");
// // 跳转到登录页
// location.href = "/#/login?redirect=/school-dashboard#/zuxia/index";
// })
// .catch(() => {
// sessionStorage.clear("401show");
// });
// }
return res
},
(err) => {
loading.close() // 关闭加载窗口
console.log(err)
ElMessage({
message: err,
type: 'error'
})
return Promise.reject(err)
}
)
const request = {
get(url: any, data: any, loadtext: any="") {
// 判断有没有参数,有参数就把参数加上
if (data) {
url = url + '?' + qs.stringify(data)
}
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.get(url)
.then(function (response) {
// console.log("封装的方法数据回来了");
// console.log(response)
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
// 上面的判断要根据实际情况修改判断对哦,不然使用else里边的reject输出,虽然结果也能正确输出,但是浏览器上会提示Uncaught (in promise),也不能继续正确的向下执行了。
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
post(url: any, data: any, loadtext: any="") {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.post(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
put(url: any, data: any, loadtext: any="") {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.put(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
},
delete(url: any, data: any, loadtext: any="") {
if (loadtext && loadtext.length > 0) {
loading.open(loadtext) // 打开加载窗口
}
return new Promise((resolve, reject) => {
axios
.delete(url, data)
.then(function (response) {
if (response.status === 200 && response.data.code === successCode) {
resolve(response.data)
} else {
reject(response.data)
}
})
.catch((e) => {
reject(e)
})
})
}
}
export default request
在ts中使用封装的请求也是一样的
引入封装的request之后使用方法如下。其实都是一样的,就是要注意一下ts中的类型检查,类型不确定时使用可以使用any,unknown等类型来处理,或者使用类型断言。
const getTasksList = async () => {
const result: any = await request.get('/growing/api/Tasks/GetTasksPage', state.pageParam, "请求")
console.log(result)
state.tasksList = result.data.data
// 请求的api地址,以及返回数据的格式自己根据自己的修改
// request.get('/education/api/Feedback/GetSchools', state.pageParam).then((res: any) => {
// console.log('获取任务')
// console.log(res)
// state.tasksList = res.data.data
// })
}
vue3的一个模板vue3-admin-template里边自带封装的request请求
贴一下他这个封装的代码,也可以考虑借鉴:
import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/store/user'
import { getCookies } from '@/utils/storage'
import defaultSettings from '@/settings'
// 业务请求
const request = axios.create({
baseURL: defaultSettings.isMockData ? '' : import.meta.env.VITE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// const request = axios.create({
// baseURL: "", // url = base url + request url
// // withCredentials: true, // send cookies when cross-domain requests
// timeout: 5000 // request timeout
// })
// request interceptor
request.interceptors.request.use(
(config) => {
// do something before request is sent
const userStore = useUserStore()
if (userStore.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = getCookies('Fanqie-Token')
}
return config
},
(error) => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
request.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
(response) => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000 && res.code!=200) {
ElMessage({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
ElMessageBox.confirm(
'You have been logged out, you can cancel to stay on this page, or log in again',
'Confirm logout',
{
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}
).then(() => {
const userStore = useUserStore()
userStore.resetToken.then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
(error) => {
console.log('err' + error) // for debug
ElMessage({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
/**
* 用于请求 gitee 的数据
*/
const requestA = axios.create({
baseURL: import.meta.env.VITE_APP_GITEE_BASE_API, // url = base url + request url
timeout: 60 * 1000
})
requestA.interceptors.request.use(
(config) => {
return config
},
(error) => {
console.log(error)
return Promise.reject(error)
}
)
requestA.interceptors.response.use(
(response) => {
const { data, status, statusText } = response
if (status === 200) {
return data
} else {
ElMessage({
message: statusText || 'Error',
type: 'error',
duration: 5 * 1000
})
return false
}
},
(error) => {
console.log('err' + error)
ElMessage({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export { request, requestA }
他这个封装的api接口请求示例:
也是像上面说的那样单独提出来一个文件写的
import { request } from '@/utils/request'
/**
* @method addUserLogin 登录
* @param {*} data
* @returns
*/
export function addUserLogin(data) {
return request({
url: '/user-login',
method: 'post',
data
})
}
/**
* @method addUserInfo 用户信息
* @param {*} data
* @returns
*/
export function addUserInfo(data) {
return request({
url: '/user-info',
method: 'post',
data
})
}
调用方法和前面的一样就不在累述了
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)