uni-app 使用v-show轮流加载使用v-for渲染的uni-grid时会出现加载不全的问题 并且官方示例也复现了问题
uni-app 使用v-show轮流加载使用v-for渲染的uni-grid时会出现加载不全的问题 并且官方示例也复现了问题
产品分类
uniapp/H5
开发环境信息
项目 | 信息 |
---|---|
PC开发环境操作系统 | Windows |
PC开发环境版本号 | win11 |
HBuilderX类型 | 正式 |
HBuilderX版本号 | 4.36 |
浏览器平台 | Chrome |
浏览器版本 | Microsoft Edge 版本 126.0.2592.87 (正式版本) (64 位) |
项目创建方式 | HBuilderX |
示例代码
<template>
<view class="viewport">
<!-- 推荐封面图 -->
<view class="cover">
<image :src="bannerPicture"></image>
</view>
<!-- 推荐选项 -->
<view class="tabs">
<text
v-for="(item, index) in subTypes"
:key="item.id"
class="text"
:class="{ active: index === activeIndex }"
@tap="activeIndex = index"
>
{{ item.title }}
</text>
</view>
<!-- 推荐列表 -->
<scroll-view
v-for="(item, index) in subTypes"
:key="item.id"
v-show="activeIndex === index"
scroll-y
class="scroll-view"
@scrolltolower="onScrolltolower"
>
<uni-grid :column="2" :showBorder="false" :square="false">
<uni-grid-item v-for="(goods, index2) in item.goodsItems.items" :key="goods.id">
<view class="goods">
<navigator hover-class="none" class="navigator" :url="`/pages/goods/goods?id=${goods.id}`">
<image class="thumb" :src="goods.picture"></image>
<view class="name ellipsis">{{ goods.name }}</view>
<view class="price">
<text class="symbol">¥</text>
<text class="number">{{ goods.price }}</text>
</view>
</navigator>
</view>
</uni-grid-item>
</uni-grid>
<view class="loading-text">
{{ item.finish ? '没有更多数据了~' : '正在加载...' }}
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { getHotListAllAPI } from '../../services/home';
import { onLoad, onReady } from '@dcloudio/uni-app';
import { ref } from 'vue';
// 热门推荐页 标题和url
const hotMap = [
{ type: '1', title: '特惠推荐', url: '/hot/preference' },
{ type: '2', title: '爆款推荐', url: '/hot/inVogue' },
{ type: '3', title: '一站买全', url: '/hot/oneStop' },
{ type: '4', title: '新鲜好物', url: '/hot/new' },
];
// uniapp 获取页面参数
const query = defineProps<{
type: string;
}>();
// 获取当前推荐信息
const currHot = hotMap.find((v) => v.type === query.type);
// 动态设置标题
uni.setNavigationBarTitle({ title: currHot!.title });
// 推荐封面图
const bannerPicture = ref('');
// 推荐选项
const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([]);
// 高亮的下标
const activeIndex = ref(0);
// 获取热门推荐数据
const getHotRecommendData = async () => {
const res = await getHotListAllAPI(currHot!.url, {
// 技巧:环境变量,开发环境,修改初始页面方便测试分页结束
page: 1,
pageSize: 10,
});
// 保存封面
bannerPicture.value = res.data.result.bannerPicture;
// 保存列表
subTypes.value = res.data.result.subTypes;
};
// 页面加载
onReady(() => {
getHotRecommendData();
});
// 滚动触底
const onScrolltolower = async () => {
// 获取当前选项
const currsubTypes = subTypes.value[activeIndex.value];
// 分页条件
if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
// 当前页码累加
currsubTypes.goodsItems.page++;
} else {
// 标记已结束
currsubTypes.finish = true;
// 退出并轻提示
return uni.showToast({ icon: 'none', title: '没有更多数据了~' });
}
// 调用API传参
const res = await getHotListAllAPI(currHot!.url, {
subType: currsubTypes.id,
page: currsubTypes.goodsItems.page,
pageSize: currsubTypes.goodsItems.pageSize,
});
// 新的列表选项
const newsubTypes = res.data.result.subTypes[activeIndex.value];
// 数组追加
currsubTypes.goodsItems.items.push(...newsubTypes.goodsItems.items);
};
</script>
操作步骤
首先使用 v-for
加载多个外层 view
,外层 view
中包裹了 uni-grid
(多个 uni-grid
独立),在外层 view
中添加 v-show
属性条件为 current == index
,添加一个按钮可以切换 current
。
预期结果
多个 grid
显示内容相同。
实际结果
一个 grid
能显示 10 条,第二个 grid
只能显示最后一条。
bug 描述
使用 v-show
轮流加载 v-for
渲染的 uni-grid
时,会出现加载不全的问题,原本两个 tag
中都是 10 条数据,但是第一个加载的能正常显示 10 条数据,第二个就只能显示最后一条数据,并且如果去掉 v-show
,或者去掉 uni-grid
会发现两个 v-for
中的数据都是正常的。
提供个完整工程,或者简化一下代码,我无法直接运行
在uni-app中使用v-show
和v-for
结合来轮流加载uni-grid
时,确实可能会遇到渲染不全的问题。这通常是由于Vue的异步更新机制和DOM渲染顺序导致的。为了解决这个问题,我们可以通过使用Vue的nextTick
方法来确保DOM更新完成后再进行操作,或者通过调整逻辑,使用v-if
来控制元素的渲染,因为v-if
会完全销毁和重建元素,这样可以避免一些由于DOM状态不一致导致的问题。
下面是一个使用v-if
代替v-show
并结合定时器来轮流显示uni-grid
的示例代码:
<template>
<view>
<uni-grid :column="3">
<uni-grid-item v-for="(item, index) in items" :key="index" v-if="currentIndex === index">
<view class="grid-content">{{ item }}</view>
</uni-grid-item>
</uni-grid>
</view>
</template>
<script>
export default {
data() {
return {
items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
currentIndex: 0,
interval: null,
};
},
mounted() {
this.startRotation();
},
beforeDestroy() {
clearInterval(this.interval);
},
methods: {
startRotation() {
this.interval = setInterval(() => {
this.currentIndex = (this.currentIndex + 1) % this.items.length;
}, 2000); // 每2秒切换一次
},
},
};
</script>
<style>
.grid-content {
display: flex;
align-items: center;
justify-content: center;
height: 100px;
background-color: #f0f0f0;
border: 1px solid #ddd;
}
</style>
在这个示例中,我们使用v-if
来控制uni-grid-item
的渲染,确保每次只有一个uni-grid-item
被渲染。currentIndex
用于跟踪当前应该显示的项的索引,interval
用于设置定时器,每2秒更新一次currentIndex
。
注意,我们在beforeDestroy
生命周期钩子中清除了定时器,以避免在组件销毁后继续执行定时任务,导致内存泄漏。
这种方法虽然使用了v-if
而不是v-show
,但可以有效地解决轮流加载时渲染不全的问题,因为v-if
会确保在每次切换时都重新渲染DOM元素,从而避免由于Vue的异步更新机制导致的问题。