在开发基于 uni-app 的跨平台应用时,网络请求是必不可少的功能之一。然而,直接使用 uni.request
方法会导致大量重复代码,且难以维护。因此,对请求进行封装显得尤为重要。本文将详细介绍如何封装 uni-app 的网络请求,帮助你打造高效、优雅的请求逻辑。
一、请求封装的意义
在实际开发中,网络请求通常会涉及以下问题:
-
重复代码:每次调用
uni.request
都需要设置 URL、请求头、超时时间等参数。 -
难以维护:当 Base URL 或超时时间需要修改时,需要逐个查找并替换。
-
错误处理分散:不同地方的请求可能需要重复处理错误逻辑,如登录状态失效、网络超时等。
通过封装请求方法,可以解决这些问题:
-
减少重复代码:将通用逻辑集中处理,减少重复代码。
-
便于维护:集中管理请求配置,修改一处即可生效。
-
统一错误处理:集中处理错误逻辑,提升用户体验。
二、uni-app 请求封装实现
以下是封装后的请求代码示例,我们将逐步解析其功能。
1. 基础配置
// 请求基础配置
const base_url = process.env.NODE_ENV === 'production'
? 'http://your scbaseurl/' // 生产环境
: process.env.NODE_ENV === 'test'
? 'http://your csbaseurl/' // 测试环境
: 'http://your hdbaseurl/'; // 开发环境(后端本地)
const timeout = 30000; // 请求超时时间:30秒
解析:
-
Base URL 的动态切换:通过
process.env.NODE_ENV
判断当前环境,动态切换 Base URL。这样可以在开发、测试和生产环境中灵活切换,无需手动修改代码。 -
超时时间:设置默认超时时间为 30 秒,可根据实际需求调整。
2. 请求头配置
export default (params) => {
let url = params.url;
let method = params.method || "get";
let data = params.data || {};
let header = {
'Token': uni.getStorageSync('token') || '', // 从本地存储获取 Token
'Content-Type': 'application/json;charset=UTF-8',
'Authorization': 'Basic c2FiZXI6c2FiZXJfc2VjcmV0', // 基础认证
...params.header // 允许用户自定义请求头
};
if (method === "post") {
header['Content-Type'] = 'application/json'; // POST 请求默认使用 JSON 格式
}
解析:
-
通用请求头:添加了
Token
和Authorization
,用于处理认证和鉴权。 -
动态请求头:允许用户通过
params.header
自定义请求头。 -
根据请求方法调整请求头:如果是 POST 请求,将
Content-Type
设置为application/json
。
3. 请求方法封装
return new Promise((resolve, reject) => {
uni.request({
url: base_url + url,
method: method,
header: header,
data: data,
timeout,
success(response) {
const { statusCode, data, errMsg } = response;
if (statusCode == 200) {
if (data.code === 0) {
resolve(data.data); // 请求成功,返回数据
} else if (data.code == -1) {
console.log(data.msg);
uni.showToast({
title: data.msg || '未知错误', // 确保 msg 是字符串
icon: "none",
duration: 10000,
});
} else {
// 根据返回的 code 处理不同情况
switch (data.code) {
case 401:
uni.navigateTo({
url: "/pages/login/login",
});
break;
case 404:
uni.showToast({
title: '请求地址不存在...',
duration: 2000,
});
break;
default:
uni.showToast({
title: '请重试...',
duration: 2000,
});
break;
}
}
} else if (statusCode == 401) {
// 未授权,跳转到登录页面
uni.navigateTo({
url: "/pages/login/login",
});
}
},
fail(err) {
console.log(err, '网络超时');
uni.showToast({
title: err.errMsg.indexOf('request:fail') !== -1 ? '网络连接失败' : '未知错误',
icon: "error",
duration: 2000
});
reject(err);
},
complete() {
// 不管成功还是失败都会执行
uni.hideLoading();
}
});
});
};
解析:
-
Promise 封装:使用
Promise
封装请求,方便在页面中使用.then()
和.catch()
处理请求结果。 -
成功处理:
-
如果状态码为 200,且返回的
data.code
为 0,表示请求成功,返回数据。 -
如果返回的
data.code
为 -1,显示错误信息。 -
根据不同的
data.code
,处理不同的错误情况(如 401 跳转到登录页面)。
-
-
失败处理:
-
捕获网络请求失败的错误,并提示用户。
-
使用
reject
将错误传递到调用处。
-
-
完成处理:
-
请求完成后,隐藏加载动画。
-
完整的代码封装:
// 全局请求封装——也可像上述一样根据环境不同的判断
// const base_url = '/api';
const base_url = '你的开发环境url';//开发环境
// const base_url = '你的测试环境url';//测试环境
// const base_url = '你的正式环境url';//正式环境
// 请求超时设置
const timeout = 30000;
export default (params) => {
let url = params.url;
let method = params.method || "get";
let data = params.data || {};
let header = {
'Token': uni.getStorageSync('token') || '',
'Content-Type': 'application/json;charset=UTF-8',
'Authorization': 'Basic c2FiZXI6c2FiZXJfc2VjcmV0',
...params.header
};
if (method === "post") {
header['Content-Type'] = 'application/json';
}
return new Promise((resolve, reject) => {
uni.request({
url: base_url + url,
method: method,
header: header,
data: data,
timeout,
success(response) {
// console.log(response, '响应')
const {
statusCode,
data,
errMsg
} = response;
if (statusCode == 200) {
if (data.code === 0) {
resolve(data.data);//需要根据后端实际接口返回数据层级去resolve
// resolve(data);
} else if (data.code == -1) {
console.log(data.msg)
uni.showToast({
title: data.msg, // 确保 msg 是字符串,如果不是则使用 '未知错误'
icon: "none",
duration: 10000,
});
} else {
// uni.clearStorageSync();
switch (code) {
case 401:
uni.navigateTo({
url: "/pages/login/login",
});
break;
case 404:
uni.showToast({
title: '请求地址不存在...',
duration: 2000,
});
break;
default:
uni.showToast({
title: '请重试...',
duration: 2000,
});
break;
}
}
} else if (statusCode == 401) {
uni.navigateTo({
url: "/pages/login/login",
});
}
},
fail(err) {
console.log(err, '网络超时');
uni.showToast({
title: err.msg.indexOf('request:fail') !== -1 ? '网络连接失败' : '网络连接失败',
icon: "error",
duration: 2000
});
reject(err);
},
complete() {
// 不管成功还是失败都会执行
uni.hideLoading();
// uni.hideToast();
}
});
});
};
4. 使用示例
封装完成后,可以在页面中轻松调用封装的请求方法。以下是一个示例:
api文件夹 => login.js
// 引入 request 文件
import request from '../request/index.js'
// 登录接口
export default function loginApi(params){
return request({
url: 'login/loginApp',
method: 'post',
data: params,
})
}
login.vue
<script>
import loginApi from "../.././api/login.js";
export default {
data() {
return {
username: '',
password: '',
};
},
methods: {
async handleSubmit() {
this.showLoading(); // 显示加载提示
let params = {
username: this.username,
password: this.password,
};
try {
let res = await loginApi(params);
// console.log(res, '登录');
if (res && res.token) {
uni.setStorageSync('token', res.token);
uni.setStorageSync('salesman', res);
uni.showToast({
title: '登录成功',
icon: 'success',
duration: 1500
});
// 设置延时,确保在提示消息显示足够的时间后执行跳转
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
});
}, 1500);
} else {
uni.showToast({
title: '登录失败',
icon: 'none',
duration: 2000
});
}
} catch (error) {
uni.showToast({
title: '登录失败,请重试',
icon: 'none',
duration: 2000
});
} finally {
this.hideLoading(); // 隐藏加载提示
}
},
// 显示加载提示
showLoading() {
uni.showLoading({
title: '登录中...',
mask: true // 显示透明蒙层,防止触摸穿透
});
},
// 隐藏加载提示
hideLoading() {
uni.hideLoading();
}
}
};
</script>
三、总结
通过封装 uni-app 的网络请求,我们实现了以下目标:
-
减少重复代码:将通用的请求逻辑集中处理,避免重复代码。
-
便于维护:集中管理请求配置,修改一处即可生效。
-
统一错误处理:集中处理错误逻辑,提升用户体验。
封装后的请求方法不仅提高了开发效率,还增强了代码的可读性和可维护性。希望本文能帮助你在 uni-app 开发中更好地实现网络请求封装。