设计思路
- 全局加载动画和提示框管理:使用Vue的插件机制,创建一个全局的加载和提示组件,通过状态管理控制其显示与隐藏,这样可以保证在不同类型请求时统一处理。
- 性能优化:
- 并发请求:利用
Promise.all
控制并发数量,避免过多请求同时发送导致性能问题。
- 分页请求:采用防抖或节流策略,避免用户频繁触发请求。同时,在服务端做缓存优化,减少重复查询数据库的开销。
- 文件上传下载:采用断点续传技术,对于大文件可以提高传输效率和稳定性。在前端可以限制同时上传/下载的文件数量。
- 网络异常处理:
- 超时处理:设置合理的超时时间,利用
Promise.race
结合setTimeout
实现。
- 重复请求:维护一个请求队列,每次发送请求前检查是否已有相同请求在队列中,若有则取消当前请求。
- 断网重连:利用
window.addEventListener('online', callback)
监听网络恢复,自动重发未完成的请求。
技术选型
- 状态管理:Vuex用于管理全局加载动画和提示框的状态,以及网络请求的状态。
- HTTP请求库:Axios,它具有丰富的功能,如拦截器、超时设置等,方便处理各种网络请求场景。
- UI库:如Element - UI或Ant - Design - Vue,用于提供加载动画和提示框组件。
核心代码架构
- Vuex状态管理
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
isLoading: false,
loadingText: '',
errorMessage: '',
requestQueue: []
},
mutations: {
SET_LOADING(state, payload) {
state.isLoading = payload.isLoading;
state.loadingText = payload.loadingText;
},
SET_ERROR(state, errorMessage) {
state.errorMessage = errorMessage;
},
ADD_REQUEST(state, requestConfig) {
state.requestQueue.push(requestConfig);
},
REMOVE_REQUEST(state, requestConfig) {
state.requestQueue = state.requestQueue.filter(config => config!== requestConfig);
}
},
actions: {
showLoading({ commit }, payload) {
commit('SET_LOADING', payload);
},
hideLoading({ commit }) {
commit('SET_LOADING', { isLoading: false, loadingText: '' });
},
showError({ commit }, errorMessage) {
commit('SET_ERROR', errorMessage);
},
addRequest({ commit }, requestConfig) {
commit('ADD_REQUEST', requestConfig);
},
removeRequest({ commit }, requestConfig) {
commit('REMOVE_REQUEST', requestConfig);
}
}
});
export default store;
- Axios拦截器
import axios from 'axios';
import store from './store';
// 请求拦截器
axios.interceptors.request.use(config => {
store.dispatch('addRequest', config);
store.dispatch('showLoading', { isLoading: true, loadingText: '正在请求数据...' });
return config;
}, error => {
store.dispatch('hideLoading');
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(response => {
store.dispatch('removeRequest', response.config);
if (store.state.requestQueue.length === 0) {
store.dispatch('hideLoading');
}
return response;
}, error => {
store.dispatch('removeRequest', error.config || {});
if (store.state.requestQueue.length === 0) {
store.dispatch('hideLoading');
}
if (error.message.includes('timeout')) {
store.dispatch('showError', '请求超时,请稍后重试');
} else if (error.code === 'ECONNABORTED' && error.message.includes('Network')) {
// 断网处理
window.addEventListener('online', () => {
// 重新发送请求
const retryRequest = error.config;
axios(retryRequest);
});
store.dispatch('showError', '网络连接中断,请检查网络');
} else {
store.dispatch('showError', '请求失败,请稍后重试');
}
return Promise.reject(error);
});
export default axios;
- 分页请求示例(结合防抖)
<template>
<div>
<button @click="fetchData">获取分页数据</button>
</div>
</template>
<script>
import axios from './axios';
import { debounce } from 'lodash';
export default {
data() {
return {
page: 1
};
},
methods: {
fetchData: debounce(async function() {
try {
const response = await axios.get(`/api/data?page=${this.page}`);
// 处理响应数据
} catch (error) {
// 错误处理
}
}, 300)
}
};
</script>
- 并发请求示例
import axios from './axios';
const request1 = axios.get('/api/data1');
const request2 = axios.get('/api/data2');
Promise.all([request1, request2])
.then(([response1, response2]) => {
// 处理两个请求的响应
})
.catch(error => {
// 处理错误
});
- 文件上传下载示例
<template>
<div>
<input type="file" @change="uploadFile">
<a href="#" @click="downloadFile">下载文件</a>
</div>
</template>
<script>
import axios from './axios';
export default {
methods: {
async uploadFile(event) {
const file = event.target.files[0];
const formData = new FormData();
formData.append('file', file);
try {
await axios.post('/api/upload', formData, {
headers: {
'Content - Type':'multipart/form - data'
}
});
} catch (error) {
// 处理上传错误
}
},
async downloadFile() {
try {
const response = await axios.get('/api/download', {
responseType: 'blob'
});
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'filename');
document.body.appendChild(link);
link.click();
} catch (error) {
// 处理下载错误
}
}
}
};
</script>