uni-app 数据请求
核心知识点
网络请求 API
uni-app 提供了 uni.request() 方法用于发起网络请求,支持 GET、POST 等多种请求方式。
1. 基本用法
// 发起 GET 请求
uni.request({
url: 'https://api.example.com/data',
method: 'GET',
data: {
page: 1,
limit: 10
},
success: (res) => {
console.log('请求成功:', res.data);
},
fail: (err) => {
console.log('请求失败:', err);
},
complete: () => {
console.log('请求完成');
}
});
// 发起 POST 请求
uni.request({
url: 'https://api.example.com/login',
method: 'POST',
data: {
username: 'admin',
password: '123456'
},
success: (res) => {
console.log('登录成功:', res.data);
},
fail: (err) => {
console.log('登录失败:', err);
}
});2. 请求参数
uni.request() 支持以下参数:
url: 请求地址method: 请求方法,默认为 GETdata: 请求数据header: 请求头dataType: 响应数据类型,默认为 jsonresponseType: 响应类型,默认为 textsuccess: 成功回调fail: 失败回调complete: 完成回调
3. 并发请求
使用 Promise.all() 可以发起并发请求:
Promise.all([
uni.request({ url: 'https://api.example.com/data1' }),
uni.request({ url: 'https://api.example.com/data2' })
]).then(([res1, res2]) => {
console.log('数据1:', res1.data);
console.log('数据2:', res2.data);
}).catch((err) => {
console.log('请求失败:', err);
});请求封装
为了提高代码复用性和可维护性,我们可以封装网络请求。
1. 基础封装
// utils/request.js
const baseURL = 'https://api.example.com';
function request(options) {
return new Promise((resolve, reject) => {
uni.request({
url: baseURL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
...options.header
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else {
reject(new Error(`请求失败: ${res.statusCode}`));
}
},
fail: (err) => {
reject(err);
}
});
});
}
// 导出常用方法
export default {
get(url, data, header) {
return request({ url, method: 'GET', data, header });
},
post(url, data, header) {
return request({ url, method: 'POST', data, header });
},
put(url, data, header) {
return request({ url, method: 'PUT', data, header });
},
delete(url, data, header) {
return request({ url, method: 'DELETE', data, header });
}
};2. 高级封装
添加拦截器和错误处理:
// utils/request.js
const baseURL = 'https://api.example.com';
// 请求队列
let requestQueue = [];
// 是否显示加载中
let loadingCount = 0;
function request(options) {
return new Promise((resolve, reject) => {
// 显示加载中
if (options.loading !== false) {
showLoading();
}
// 请求开始
const requestTask = uni.request({
url: baseURL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': uni.getStorageSync('token') || '',
...options.header
},
success: (res) => {
// 请求成功处理
if (res.statusCode === 200) {
const data = res.data;
if (data.code === 0) {
resolve(data.data);
} else {
// 业务错误
uni.showToast({
title: data.message || '请求失败',
icon: 'none'
});
reject(new Error(data.message || '请求失败'));
}
} else if (res.statusCode === 401) {
// 未授权
uni.navigateTo({
url: '/pages/login/login'
});
reject(new Error('未授权'));
} else {
// 其他错误
uni.showToast({
title: `请求失败: ${res.statusCode}`,
icon: 'none'
});
reject(new Error(`请求失败: ${res.statusCode}`));
}
},
fail: (err) => {
// 网络错误
uni.showToast({
title: '网络错误',
icon: 'none'
});
reject(err);
},
complete: () => {
// 隐藏加载中
if (options.loading !== false) {
hideLoading();
}
// 移除请求任务
const index = requestQueue.indexOf(requestTask);
if (index > -1) {
requestQueue.splice(index, 1);
}
}
});
// 添加到请求队列
requestQueue.push(requestTask);
});
}
// 显示加载中
function showLoading() {
if (loadingCount === 0) {
uni.showLoading({
title: '加载中...',
mask: true
});
}
loadingCount++;
}
// 隐藏加载中
function hideLoading() {
loadingCount--;
if (loadingCount === 0) {
uni.hideLoading();
}
}
// 取消所有请求
export function cancelAllRequest() {
requestQueue.forEach(task => {
task.abort();
});
requestQueue = [];
}
// 导出常用方法
export default {
get(url, data, header) {
return request({ url, method: 'GET', data, header });
},
post(url, data, header) {
return request({ url, method: 'POST', data, header });
},
put(url, data, header) {
return request({ url, method: 'PUT', data, header });
},
delete(url, data, header) {
return request({ url, method: 'DELETE', data, header });
}
};数据处理
处理网络请求返回的数据,包括数据转换、缓存等。
1. 数据转换
// 处理列表数据
function processListData(data) {
return data.map(item => ({
id: item.id,
title: item.title,
content: item.content,
createdAt: formatDate(item.created_at),
author: {
id: item.author.id,
name: item.author.name,
avatar: item.author.avatar
}
}));
}
// 格式化日期
function formatDate(dateString) {
const date = new Date(dateString);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
}2. 数据缓存
使用本地存储缓存数据:
// 缓存数据
function cacheData(key, data, expire = 3600000) {
uni.setStorageSync(key, {
data,
expire: Date.now() + expire
});
}
// 获取缓存数据
function getCacheData(key) {
const cached = uni.getStorageSync(key);
if (!cached) return null;
if (Date.now() > cached.expire) {
// 缓存过期
uni.removeStorageSync(key);
return null;
}
return cached.data;
}
// 示例:带缓存的请求
async function getDataWithCache(url, data) {
const cacheKey = `cache_${url}_${JSON.stringify(data)}`;
// 尝试从缓存获取
const cachedData = getCacheData(cacheKey);
if (cachedData) {
return cachedData;
}
// 发起请求
const result = await request.get(url, data);
// 缓存数据
cacheData(cacheKey, result);
return result;
}实用案例
对接后端 API
1. 用户登录
<template>
<view class="login-container">
<view class="form-item">
<text>用户名</text>
<input v-model="username" placeholder="请输入用户名" />
</view>
<view class="form-item">
<text>密码</text>
<input v-model="password" type="password" placeholder="请输入密码" />
</view>
<button class="login-button" @click="login">登录</button>
</view>
</template>
<script>
import request from '../../utils/request';
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
async login() {
if (!this.username || !this.password) {
uni.showToast({
title: '请输入用户名和密码',
icon: 'none'
});
return;
}
try {
const res = await request.post('/login', {
username: this.username,
password: this.password
});
// 保存 token
uni.setStorageSync('token', res.token);
uni.setStorageSync('user', res.user);
// 登录成功,跳转到首页
uni.showToast({
title: '登录成功',
icon: 'success'
});
uni.switchTab({
url: '/pages/index/index'
});
} catch (error) {
console.error('登录失败:', error);
}
}
}
};
</script>
<style scoped>
.login-container {
padding: 40rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.form-item text {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
color: #333;
}
.form-item input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.login-button {
width: 100%;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 32rpx;
margin-top: 40rpx;
}
</style>2. 获取列表数据
<template>
<view class="list-container">
<view class="list-item" v-for="item in list" :key="item.id">
<text class="item-title">{{ item.title }}</text>
<text class="item-content">{{ item.content }}</text>
<text class="item-time">{{ item.createdAt }}</text>
</view>
<view class="loading" v-if="loading">加载中...</view>
<view class="no-more" v-if="!loading && !hasMore">没有更多数据了</view>
</view>
</template>
<script>
import request from '../../utils/request';
export default {
data() {
return {
list: [],
page: 1,
limit: 10,
loading: false,
hasMore: true
};
},
onLoad() {
this.loadData();
},
onReachBottom() {
if (!this.loading && this.hasMore) {
this.loadData();
}
},
methods: {
async loadData() {
if (this.loading) return;
this.loading = true;
try {
const res = await request.get('/list', {
page: this.page,
limit: this.limit
});
if (res.length > 0) {
this.list = [...this.list, ...res];
this.page++;
this.hasMore = res.length === this.limit;
} else {
this.hasMore = false;
}
} catch (error) {
console.error('获取数据失败:', error);
uni.showToast({
title: '获取数据失败',
icon: 'none'
});
} finally {
this.loading = false;
}
}
}
};
</script>
<style scoped>
.list-container {
padding: 20rpx;
}
.list-item {
background-color: #fff;
border-radius: 8rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
.item-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
color: #333;
}
.item-content {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
line-height: 1.5;
}
.item-time {
font-size: 24rpx;
color: #999;
}
.loading {
text-align: center;
padding: 20rpx;
color: #666;
}
.no-more {
text-align: center;
padding: 20rpx;
color: #999;
}
</style>3. 提交表单数据
<template>
<view class="form-container">
<view class="form-item">
<text>标题</text>
<input v-model="form.title" placeholder="请输入标题" />
</view>
<view class="form-item">
<text>内容</text>
<textarea v-model="form.content" placeholder="请输入内容" />
</view>
<button class="submit-button" @click="submitForm">提交</button>
</view>
</template>
<script>
import request from '../../utils/request';
export default {
data() {
return {
form: {
title: '',
content: ''
}
};
},
methods: {
async submitForm() {
if (!this.form.title || !this.form.content) {
uni.showToast({
title: '请填写完整信息',
icon: 'none'
});
return;
}
try {
await request.post('/create', this.form);
uni.showToast({
title: '提交成功',
icon: 'success'
});
// 重置表单
this.form = {
title: '',
content: ''
};
} catch (error) {
console.error('提交失败:', error);
uni.showToast({
title: '提交失败',
icon: 'none'
});
}
}
}
};
</script>
<style scoped>
.form-container {
padding: 20rpx;
}
.form-item {
margin-bottom: 30rpx;
}
.form-item text {
display: block;
margin-bottom: 10rpx;
font-size: 28rpx;
color: #333;
}
.form-item input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.form-item textarea {
width: 100%;
height: 200rpx;
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
resize: none;
}
.submit-button {
width: 100%;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
font-size: 32rpx;
margin-top: 40rpx;
}
</style>学习目标
通过本集的学习,你应该能够:
- 掌握 uni-app 网络请求 API 的基本用法,包括 GET、POST 等请求方式
- 学会封装网络请求,添加拦截器和错误处理
- 熟悉数据处理的方法,包括数据转换和缓存
- 能够通过实用案例对接后端 API,实现用户登录、获取列表数据、提交表单等功能
- 了解网络请求的最佳实践,提高应用的性能和用户体验
小结
数据请求是 uni-app 开发中的重要部分,合理的请求封装和数据处理可以提高应用的性能和可维护性。通过本集的学习,你已经掌握了网络请求 API、请求封装、数据处理等核心知识点,并通过实际案例了解了如何对接后端 API。在后续的开发中,你可以根据实际需求,灵活运用这些知识,构建更加功能强大的应用。