行业知识
fetch和axios的区别和封装一个功能齐全的axios
2026-11-10 17:45:52
来源:互联网转载
axios和fetch区别
- 概念不同
- Fetch是一种新的获取资源的接口方式,可以直接使用
- Axios是一个基于XMLHttpRequest封装的工具包,需要引入才可以使用
- 传递数据的方式不同
- Fetch则是需要放在body属性中,以字符串的方式进行传递
- Axios是放到data属性里,以对象的方式进行传递
- 响应超时
- Fetch需要通过AbortController来设置
- Axios是直接设置timeout就可以
- 对数据的转化
- Fetch则不同,它需要使用者进行手动转化,arrayBuffer(),blob(),json(),text(),formData()
- Axios还有非常好的一点就是会自动对数据进行转化
- HTTP拦截器
- Fetch没有拦截器功能,但是要实现该功能并不难,直接重写全局Fetch方法就可以办到
- Axios设置拦截器非常简单,通过axios.interceptors.request.use(() => {})
封装axios
- 优化配置,设置默认配置项(responseType、跨域携带cookie、token、超时设置),统一设置请求头、 baseURL
- 添加请求拦截器、响应拦截器
- 不同请求方法,参数格式一致
- 全局的loading配置
- 支持下载文件
- 取消重复请求(取消请求详解)
- 可进行二次确认(一般删除、重置等方法防止用户误触会进行二次确认)
- 统一处理错误,http状态码和贴近业务的一些错误状态)
// http.jsimport axios from 'axios';import { MessageBox, Toast } from 'element-ui'import qs from qs;// 环境的切换,也可以写到.env文件中if (process.env.NODE_ENV == 'development') { axios.defaults.baseURL = 'https://www.baidu.com';} else if (process.env.NODE_ENV == 'debug') { axios.defaults.baseURL = 'https://www.ceshi.com';} else if (process.env.NODE_ENV == 'production') { axios.defaults.baseURL = 'https://www.production.com';}let http = axios.create({ withCredentials: true, // 允许把cookie传递到后台, timeout: 1000 * 12});http.defaults.timeout = 10000;// 设置post请求头http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';// 请求拦截器http.interceptors.request.use( config => { // 设置 token const token = localStorage.getItem('token') token && (config.headers.Authorization = token) // 记录请求,为后续在响应拦截器中取消做准备 addPendingRequest(config) return config; }, error => { // 对请求错误做些什么 return Promise.reject(error); });// 响应拦截器http.interceptors.response.use( response => { // 取消重复请求 removePendingRequest(response.config); if (response.data instanceof Blob) { // 处理二进制文件 return downloadFile(response); } if (response.status === 200) { return Promise.resolve(response); } else { return Promise.reject(response); } }, error => { // 服务器状态码不是2开头的的情况 const { response } = error;if (response) {// 请求已发出,但是不在2xx的范围 errorHandle(response.status, response.data.message);return Promise.reject(response);} else {// 处理断网的情况if (!window.navigator.onLine) { return Promise.reject(new Error('请检查网络连接'))} else {return Promise.reject(error);}} });// 错误处理const errorHandle = (status, other) => {// 状态码判断switch (status) {// 401: 未登录状态,跳转登录页case 401:toLogin();break;// 403 token过期// 清除token并跳转登录页case 403:Toast('登录过期,请重新登录');localStorage.removeItem('token');store.commit('loginSuccess', null);setTimeout(() => {toLogin();}, 1000);break;// 404请求不存在case 404:Toast('请求的资源不存在'); break;default:console.log(other); }}// 在接口中传递参数,指定是否需要二次确认export const post = (url, params, confirm = false) => { return new Promise((resolve, reject) => { if (confirm || confirm.confirm) { MessageBox.confirm(confirm.confirm || '确认操作吗', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) .then(() => { resolve(http.post(url, params)) }) .catch(_ => { console.log('取消请求') }) } else { resolve(http.post(url, params)) } })}// 取消重复请求let repeatRequests = {}function generateRequestKey(config) { const { method, url, params, data } = config const split = '---' const array = [`url:`, url, `${split}method:`, method] params && array.push(`${split}params:`, qs.stringify(params)) data && array.push(`${split}data:`, typeof data === 'object' ? JSON.stringify(data) : data) return array.join('')}function addPendingRequest(config) { const requestKey = generateRequestKey(config) config.cancelToken = new axios.CancelToken(cancel => { !repeatRequests[requestKey] && (repeatRequests[requestKey] = []) repeatRequests[requestKey].push(cancel) }) return config}function removePendingRequest(config) { const requestKey = generateRequestKey(config) const needCancel = repeatRequests[requestKey]?.length > 1 if (needCancel) { // 不重复,不取消 repeatRequests[requestKey].forEach(cancel => { cancel(requestKey) }) } needCancel ? (repeatRequests[requestKey] = []) : (repeatRequests = {})}// 文件下载const downloadFile = (response) => { console.log("response.data.type:", response.data.type); return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = function () { try { console.log("result:", this.result); const jsonData = JSON.parse(this.result); // 成功 说明是普通对象数据 if (jsonData?.code !== 200) { Message.error(jsonData?.message ?? "请求失败"); reject(jsonData); } } catch (err) { // 解析成对象失败,说明是正常的文件流 const blob = new Blob([response.data]); // 本地保存文件 const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; const filename = response?.headers?.["content-disposition"] ?.split("filename*=")?.[1] ?.substr(7); link.setAttribute("download", decodeURI(filename)); document.body.appendChild(link); link.click(); resolve(response.data); } }; fileReader.readAsText(response.data); });};export default http;// api.jsimport request from "./http.js";// 文件下载export const exportFile = (data) => request.post("/exportfile", data, { responseType: "blob", });
上一篇:宝塔面板怎么重新装?