欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > HBuilderX(uni-app)搭建小程序流程(请求封装,下拉刷新,底部加载等)

HBuilderX(uni-app)搭建小程序流程(请求封装,下拉刷新,底部加载等)

2025/3/19 0:38:56 来源:https://blog.csdn.net/xiaolouuuu/article/details/144376818  浏览:    关键词:HBuilderX(uni-app)搭建小程序流程(请求封装,下拉刷新,底部加载等)

uni-app 是一个跨平台的前端框架,用于开发多端应用。uni-app 提供了一个统一的 API,允许开发者编写一次代码,最终部署到多个平台,极大地提高了开发效率。在编写小程序的时候和原生微信开发者工具语法略有区别。

目录

一、请求工具封装

二、点击跳转页面报错MiniProgramError{"errMsg":"navigateTo:fail timeout"}

三、滚动底部发请求和下拉刷新

四、接收路由参数 

五、引入vant3


一、请求工具封装

网络请求官网: 

网络请求:   uni.request(OBJECT) | uni-app官网

uni-app 中进行网络请求时,可以通过封装请求工具来简化和统一请求的逻辑。封装后的请求工具可以处理常见的请求配置、错误处理、响应数据处理等,使得网络请求的管理更加清晰和高效。

1、不封装的请求示例

uni.request({url: 'https://www.example.com/request',  // 目标 URL 地址data: {text: 'uni.request'  // 请求发送的数据(可以是 GET 请求的查询参数,或 POST 请求的请求体)},header: {'custom-header': 'hello'  // 自定义请求头,向服务器发送附加的头部信息},success: (res) => {console.log(res.data);  // 请求成功时,打印服务器响应的数据this.text = 'request success';  // 更新组件的文本内容为 'request success'}
});
  • url:请求的目标地址。https://www.example.com/request 是一个示例 URL,表示请求发送到 https://www.example.com/request 这个接口。这个地址是服务器上公开的接口,uni.request 会向该地址发送 HTTP 请求。

  • data:请求中发送的数据。在这个例子中,data: { text: 'uni.request' } 表示发送一个包含字段 text 和值 'uni.request' 的对象。

    • 如果是 GET 请求,data 会被编码为查询字符串,如 ?text=uni.request
    • 如果是 POST 请求,data 会作为请求体发送。
  • header:请求头部信息。在这个例子中,设置了一个自定义请求头 'custom-header': 'hello',它将作为请求的一个头部传递给服务器。这通常用于身份验证、内容类型设置等场景。

  • success:这是请求成功后的回调函数。在请求成功并收到服务器响应后,会执行 success 函数。

    • res 是返回的响应对象,包含了服务器返回的数据。
    • console.log(res.data):打印服务器响应的数据,通常是 JSON 格式的数据。
    • this.text = 'request success':这行代码将组件的 text 属性更新为 'request success',通常会用于更新页面显示的内容(例如,页面上的文本会显示为 'request success')。

2、在文件结构中新建

3、请求工具封装

src/utils/api/api.js中:

// src/utils/api.jsconst baseURL = "http://localhost:8082/api";  // 确保你的基础 URL 正确// 请求封装函数
const request = (url, method = 'GET', data = {}) => {return new Promise((resolve, reject) => {uni.request({url: baseURL + url,method: method,data: data,success(res) {if (res.statusCode === 200) {resolve(res.data);  // 返回数据} else {reject(res);  // 如果不是 200,返回错误信息}},fail(err) {reject(err);  // 请求失败时}})});
};export { request };

4、请求工具封装优化(可选)

// src/utils/api.jsconst baseURL = "http://localhost:8082/api";  // 确保你的基础 URL 正确// 请求封装函数
const request = (url, method = 'GET', data = {}, showLoading = true) => {// 请求拦截器:可以在这里添加请求头、token等信息const token = uni.getStorageSync('token'); // 假设你存储了tokenconst headers = {'Authorization': token ? `Bearer ${token}` : '', // 如果有token,自动添加到请求头'Content-Type': 'application/json'  // 默认发送 JSON 格式的数据};// 在请求开始时,显示加载中(如果需要)if (showLoading) {uni.showLoading({title: '加载中...',mask: true});}return new Promise((resolve, reject) => {uni.request({url: baseURL + url,method: method,data: data,header: headers,  // 请求头success(res) {uni.hideLoading();  // 请求完成后关闭加载提示if (res.statusCode === 200) {resolve(res.data);  // 成功时返回数据} else {// 处理非 200 状态码的响应const errorMessage = res.data?.message || '请求失败';uni.showToast({title: errorMessage,icon: 'none'});reject(res);  // 返回错误信息}},fail(err) {uni.hideLoading();  // 请求失败时也关闭加载提示// 网络请求失败时的错误处理uni.showToast({title: '网络错误,请稍后再试',icon: 'none'});reject(err);  // 返回错误信息}});});
};export { request };

5、统一管理接口

src/utils/api/apiMethod.js中:

// src/utils/api/apiMethod.js
import { request } from '@/utils/api.js'; // 封装 getLunbo 请求
export const getLunbo = () => {return request('/getlunbo');
};// 封装 getGoods 请求
export const getGoods = () => {return request(`/getgoods?pageindex=${pageIndex}`);
};

6、页面中引入对应的请求函数

二、点击跳转页面报错MiniProgramError{"errMsg":"navigateTo:fail timeout"}

	<div class="nav-item" @click="goToShoplist"><span class="iconfont icon-ziyuan"></span><span>商品列表</span></div>
const goToShoplist=()=> {// UniApp页面跳转uni.navigateTo({url: '/pages/shoplist/shoplist'  // 跳转到 shoplist 页面});
}

 绑定跳转事件后,点击报错:

这个问题通常是由于小程序页面栈已满导致的。uni.navigateTo 是用于跳转到一个非 tabBar 页面,如果页面栈已经有很多页面,可能会超时,因为小程序的页面栈有大小限制。

navigateTo:fail timeout 错误表明跳转超时,可能是因为页面栈已经很满,无法再添加新页面。以下是可能的解决方法:

const goToShoplist = () => {// 使用 uni.redirectTo 跳转,替换当前页面uni.redirectTo({url: '/pages/shoplist/shoplist'  // 跳转到 shoplist 页面});
};

如果你频繁进行页面跳转,可能需要通过 uni.reLaunchuni.redirectTo 来清理页面栈,避免栈过多造成超时。

三、滚动底部发请求和下拉刷新

 此模板用的 插件市场:全端通用的精美的商品列表 - DCloud 插件市场

本人根据需求略作修改 ,完整代码如下:

<template><view class="content"><scroll-view refresher-enabled@refresherrefresh="refresherrefresh":refresher-triggered="isTriggered"class="scroll-view" scroll-y="true" @scrolltolower="onReachBottom"style="height: 100vh;"><view class="t-goods-list" v-if="goodsList&&goodsList.length>0"  ><view class="t-goods-item" v-for="(item,index) in goodsList" :key="index" @click="clickItem(index)"><image class="t-goods-img" :src="item.goodsPic"></image><view class="t-goods-name"><text>{{item.goodsName}}</text></view><view class="t-goods-desc"><text>{{item.goodsDesc}}</text></view><view class="t-goods-tags" v-if="item.tags && item.tags.length>0"><view class="t-tag" v-for="(tag,idx) in item.tags" :key="idx":style="[tag.type === 'hot'?{'border-color':'#f499a8',color:'#d00a2b'}:{'border-color':'#999999',color:'#000000'},{'max-width': item.tags.length>1?'148rpx':'100%'}]"><text>{{tag.name}}</text></view></view><view class="t-goods-price"><text class="t-rmb">¥</text><text class="t-p1">{{item.goodsPrice}}</text><text class="t-p2">¥{{item.goodsOriginPrice}}</text></view><view class="t-rate"><view><text>{{item.rateNum}}人评价</text></view><view class="t-rate-percent"><text>{{item.ratePercent}}好评</text></view></view></view></view><view class="t-empty" v-else-if="isInit"><image src="/static/goods.png"></image><view class="t-empty-desc">没有商品哦~</view></view><view class="t-loading-more" v-if="isLoading || isNoMore && goodsList && goodsList.length>0"><image src="../../static/loading.png" v-if="isLoading"></image><view class="t-loading-desc" v-if="isLoading || isNoMore">{{isLoading?'加载中...':(isNoMore?'没有更多数据了':'')}}</view></view></scroll-view></view>
</template><script setup>
import { ref , reactive, onMounted } from 'vue';  // 引入 ref 和 onMounted
import { getLunbo , getGoods } from '@/utils/apiMethod.js';// 商品列表的数据状态
const goodsList = ref([]);
const newGoodsList = ref([]);
const pageNo = ref(1);
const pageSize = ref(10);
const isNoMore = ref(false);
const isLoading = ref(false);
const isInit = ref(false);
const isTriggered=ref(false)// 页面加载时获取商品数据
onMounted(() => {loadGoods();
});const refresherrefresh = () => {isTriggered.value = true;  // 表示下拉刷新触发console.log('拉拉拉');  // 打印日志// 重置页码为 1,准备重新加载数据pageNo.value = 1;loadGoods()setTimeout(()=>{isNoMore.value = false;  // 主动关闭加载更多的标志,表示可以继续加载更多isTriggered.value = false;  },1000)};// 页面滑动到底部加载更多数据
const onReachBottom = () => {if (isNoMore.value) return;console.log('原来的pageNo.value',pageNo.value);isLoading.value = true;setTimeout(async() => {pageNo.value += 1;console.log('新的pageNo.value',pageNo.value);const res = await getGoods(pageNo.value)console.log('请求商品数据',res.message);newGoodsList.value = res.message.map(item => {return {       goodsPic: item.img_url,  goodsName: item.title,goodsDesc: item.zhaiyao,tags: [{ name: '限时直降300元', type: 'hot' },{ name: '赠积分', type: 'default' }],goodsPrice: item.sell_price,goodsOriginPrice: item.sell_price,rateNum: 1068,ratePercent: '99%',};});console.log('newData',newGoodsList.value);goodsList.value = [...goodsList.value, ...newGoodsList.value];isLoading.value = false;if (res.message.length==0) {isNoMore.value = true;}}, 1500);
};// 页面初始化加载商品数据
const loadGoods = () => {isLoading.value = true;setTimeout(async() => {const res = await getGoods(pageNo.value)console.log('请求商品数据',res.message);goodsList.value  = res.message.map(item => {return {       goodsPic: item.img_url,  goodsName: item.title,goodsDesc: item.zhaiyao,tags: [{ name: '限时直降300元', type: 'hot' },{ name: '赠积分', type: 'default' }],goodsPrice: item.sell_price,goodsOriginPrice: item.sell_price,rateNum: 1068,ratePercent: '99%',};});console.log('处理后的商品列表',goodsList.value);isInit.value = true;isLoading.value = false;if (!goodsList.value || goodsList.value.length < pageSize.value) {isNoMore.value = true;}}, 1500);
};// 商品点击事件
const clickItem = (index) => {console.log("当前点击的商品下标是" + index);
};
</script><style lang="less" scoped>.content {box-sizing: border-box;min-width: 100vw;min-height: 100vh;background-color: #f2f3f5;padding-bottom: 80rpx;.t-goods-list {padding-top: 18rpx;box-sizing: border-box;width: 690rpx;margin: 0 auto;display: flex;flex-direction: row;justify-content: space-between;align-items: center;flex-wrap: wrap;.t-goods-item {box-sizing: border-box;width: 336rpx;height: 630rpx;background-color: #ffffff;border-radius: 20rpx;overflow: hidden;margin-bottom: 18rpx;position: relative;.t-goods-img {width: 100%;height: 340rpx;}.t-goods-name {font-size: 28rpx;color: #000000;line-height: 30rpx;-webkit-line-clamp: 2;display: -webkit-box;text-overflow: ellipsis;overflow: hidden;-webkit-box-orient: vertical;padding: 0rpx 18rpx;box-sizing: border-box;word-break: break-all;}.t-goods-desc {font-size: 24rpx;color: #9e9e9e;line-height: 26rpx;-webkit-line-clamp: 1;display: -webkit-box;text-overflow: ellipsis;overflow: hidden;-webkit-box-orient: vertical;padding: 0rpx 18rpx;box-sizing: border-box;margin-top: 16rpx;word-break: break-all;}.t-goods-tags {padding: 0rpx 18rpx;box-sizing: border-box;margin-top: 16rpx;.t-tag {font-size: 20rpx;border-radius: 4rpx;border: 1rpx solid;box-sizing: border-box;padding: 3rpx 5rpx;-webkit-line-clamp: 1;display: -webkit-box;text-overflow: ellipsis;overflow: hidden;-webkit-box-orient: vertical;float: left;word-break: break-all;&:not(:last-child) {margin-right: 8rpx;}}}.t-goods-price {padding: 0rpx 18rpx;box-sizing: border-box;display: flex;flex-direction: row;justify-content: flex-start;align-items: center;position: absolute;width: 100%;bottom: 70rpx;left: 0rpx;width: 100%;.t-rmb {font-size: 26rpx;color: #000000;}.t-p1 {font-size: 32rpx;color: #000000;margin-left: 4rpx;font-weight: bold;}.t-p2 {font-size: 20rpx;color: #999999;margin-left: 10rpx;text-decoration: line-through;}}.t-rate {padding: 0rpx 18rpx;box-sizing: border-box;display: flex;flex-direction: row;justify-content: flex-start;align-items: center;position: absolute;width: 100%;bottom: 30rpx;left: 0rpx;width: 100%;font-size: 20rpx;color: #999999;.t-rate-percent {margin-left: 8rpx;}}}}}.t-empty {width: 100%;height: 100%;background-color: #fff;display: flex;flex-direction: column;justify-content: center;align-items: center;image {width: 320rpx;height: 320rpx;}.t-empty-desc {padding-bottom: 30rpx;padding-top: 20rpx;font-size: 28rpx;color: #999999;}}.t-loading-more {box-sizing: border-box;display: flex;flex-direction: row;justify-content: center;align-items: center;width: 100%;height: 60rpx;image {width: 36rpx;height: 36rpx;-webkit-animation: spin 1s 0s step-end infinite;animation: spin 1s 0s step-end infinite;margin-right: 12rpx;}.t-loading-desc {font-size: 28rpx;color: #999999;}@-webkit-keyframes spin {0% {-webkit-transform: rotate(0deg);transform: rotate(0deg);}8% {-webkit-transform: rotate(30deg);transform: rotate(30deg);}16% {-webkit-transform: rotate(60deg);transform: rotate(60deg);}24% {-webkit-transform: rotate(90deg);transform: rotate(90deg);}32% {-webkit-transform: rotate(120deg);transform: rotate(120deg);}40% {-webkit-transform: rotate(150deg);transform: rotate(150deg);}48% {-webkit-transform: rotate(180deg);transform: rotate(180deg);}56% {-webkit-transform: rotate(210deg);transform: rotate(210deg);}64% {-webkit-transform: rotate(240deg);transform: rotate(240deg);}73% {-webkit-transform: rotate(270deg);transform: rotate(270deg);}82% {-webkit-transform: rotate(300deg);transform: rotate(300deg);}91% {-webkit-transform: rotate(330deg);transform: rotate(330deg);}100% {-webkit-transform: rotate(1turn);transform: rotate(1turn);}}@keyframes spin {0% {-webkit-transform: rotate(0deg);transform: rotate(0deg);}8% {-webkit-transform: rotate(30deg);transform: rotate(30deg);}16% {-webkit-transform: rotate(60deg);transform: rotate(60deg);}24% {-webkit-transform: rotate(90deg);transform: rotate(90deg);}32% {-webkit-transform: rotate(120deg);transform: rotate(120deg);}40% {-webkit-transform: rotate(150deg);transform: rotate(150deg);}48% {-webkit-transform: rotate(180deg);transform: rotate(180deg);}56% {-webkit-transform: rotate(210deg);transform: rotate(210deg);}64% {-webkit-transform: rotate(240deg);transform: rotate(240deg);}73% {-webkit-transform: rotate(270deg);transform: rotate(270deg);}82% {-webkit-transform: rotate(300deg);transform: rotate(300deg);}91% {-webkit-transform: rotate(330deg);transform: rotate(330deg);}100% {-webkit-transform: rotate(1turn);transform: rotate(1turn);}}}
</style>

 重点讲一下我添加的下拉刷新功能:

<scroll-view> 是一个可滚动的视图组件,可以容纳大量内容并允许在内容溢出时滚动查看。在 uni-app 中,<scroll-view> 组件支持下拉刷新和上拉加载等功能。 

属性与事件的解释:
  1. refresher-enabled:

    用途: 如果你希望在用户下拉时触发刷新事件,必须将这个属性设置为 true
  2. @refresherrefresh="refresherrefresh":

    用途: 当用户拉动屏幕时触发下拉刷新,你需要提供一个事件处理函数(例如 refresherrefresh)来处理刷新逻辑。
  3. :refresher-triggered="isTriggered":

    用途: 你可以通过 isTriggered 的值来控制刷新动画的显示与隐藏。一般情况下,当数据加载完成后,需要将 isTriggered 设置为 false,从而结束下拉刷新动画。

 设置refresherrefresh事件和isTriggered属性控制:

const isTriggered=ref(false)// 表示下拉动画
const refresherrefresh = () => {isTriggered.value = true;  // 表示下拉刷新触发console.log('拉拉拉');  // 打印日志// 重置页码为 1,准备重新加载数据pageNo.value = 1;loadGoods()setTimeout(()=>{isNoMore.value = false;  // 主动关闭加载更多的标志,表示可以继续加载更多isTriggered.value = false;  },1000)};

四、接收路由参数 

uni-app(HBuilderX)Vue3路由传参和接收路由参数!!-CSDN博客

五、引入vant3

保姆级教程!HBuilder X(uniapp)微信小程序vue3项目引入vant !!-CSDN博客

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词