0

0

聊聊uniapp的scroll-view下拉加载

青灯夜游

青灯夜游

发布时间:2022-07-14 21:07:08

|

4299人浏览过

|

来源于掘金社区

转载

聊聊uniapp的scroll-view下拉加载

最近在做一个微信小程序的直播模块,模块里的聊天室功能是用scroll-view + 一维数组的形式展示的,而且也没有进行任何的优化,导致用户的体验感比较差

首先模拟一下优化前的聊天室情况

1.gif

肉眼可见的蛋疼~

但是优化还是得优化滴,不优化是不可能滴,但是在开始之前,我觉得有必要把优化步骤拆分为以下两点?

1. 不再使用scroll-into-view设置锚点

由于旧版本使用的是scroll-view + 一维数组的形式实现的,这就导致在数据添加后页面总会显示加载后的最后一条信息,而不是加载前的最后一条信息,因此上一任开发者使用了scroll-into-view属性作为数据加载后的回位锚点,但是由于锚点指向的切换和数据加载并不是同步发生的,这就导致出现回弹的现象

2. 大量数据的处理

因为是聊天室功能,因此不可避免的需要加载大量的用户对话、图片等内容,又因为scroll-view本身并不适合加载大量的数据(太菜了想不出来其他办法),故而需要在数据的加载和显示部分下点功夫处理一下

3. 附加功能处理

聊天室原本还有返回底部等功能存在,因此在完成优化后原本的功能也不能忽略

OK开工~

1、倒置scroll-view

为什么要倒置scroll-view呢?从上面的第一点我们可以看出,如果需要正序地插入数据,那么就会不可避免地出现数据加载后无法显示后面数据的情况,但是想要解决这种情况又需要使用scroll-into-view属性,那么如果需要彻底地解决这个问题,就需要从问题的根源scroll-view下手

首先是修改前的代码?

这是一个直播画面
  
    
      {{ item.data }}
    
  
const scrollIntoView = ref("index1");
const upper = () => {
  let lastNum = scrollData.value[0].data;
  let newArr = [];
  for (let index = 1; index <= 10; index++) {
    newArr.push({
      color: getRandomColor(),
      data: lastNum + index,
      index: `index${lastNum + index}`,
    });
  }
  scrollData.value.unshift(...newArr.reverse());
  // 这里可以使用nextTick来替换一下,结果也是一样的,但是为了更明显的回弹效果我使用了定时器
  setTimeout(() => {
    scrollIntoView.value = `index${lastNum}`;
    console.log("scrollIntoView  :>>", scrollIntoView.value);
  }, 100);
};
const getRandomColor = () => {
  return "#" + Math.random().toString(16).substr(2, 6);
};

那么就先来试一下倒置scroll-view到底也没有效果

首先我们需要给scroll-view套上一个transform:rotate(180deg)的属性,然后再给内部的子元素也套上同样的属性,别忘了给存放数据的数组也倒置一下,最重要的,把scroll-view上的scroll-into-view属性去掉,就会得到这样的效果?

2.gif

还有就是此时滚动条的位置是在左边的,如果有需要可以使用CSS属性去掉,或者自行模拟,下面是去去除滚动条的CSS样式?

::-webkit-scrollbar {
  display:none;
  width:0;
  height:0;
  color:transparent;
}

到这里还只是第一步,下一步是如何下拉加载数据

此时我们的scroll-view是处于倒置的状态,也就是说顶部是底,底部才是顶(搁着绕口令呢),所以之前使用的scrolltoupper触顶方法要替换成scrolltolower触底方法才能实现“下拉加载”

3.gif

下面是目前的聊天室看起来好多了

4.gif

Evoker
Evoker

一站式AI创作平台

下载

2、大量数据的处理

处理完回弹问题后,就需要考虑如何处理大量数据。由于uni-app官方也在文档中提到scroll-view加载大批量数据的时候性能较差,但无奈手头上也没有别的办法,只能死马当活马医了

我第一个想法就是非常经典的虚拟列表,但是此前所看的很多关于虚拟列表的文章都是在web端实现的,似乎小程序领域里并不是一个被经常采用的方法,但是所幸还是找到了如何在微信小程序实现虚拟列表的资料,详情可以查看这篇文章?微信小程序虚拟列表

OK说干就干,那么第一步就是要明确实现虚拟列表需要什么样的数据结构,虚拟列表其实简单地说就是当某一个模块的数据超出了可视范围就将其隐藏,那么如何将数据分为多个模块呢?答案就是二维数组

首先将当前的页码存储起来(默认为0),当触发下拉加载动作时页码+1,然后以当前页码作为下标存入数组

const currentShowPage=ref(0)
const upper = () => {
  let len = scrollData.value[currentShowPage.value].length - 1;
  let lastNum = scrollData.value[currentShowPage.value][len].data;
  let newArr = [];
  currentShowPage.value += 1;
  for (let index = 1; index <= 10; index++) {
    newArr.push({
      color: getRandomColor(),
      data: lastNum + index,
      index: `index${lastNum + index}`,
    });
  }
  scrollData.value[currentShowPage.value] = newArr;
};

当然别忘了在页面中也需要以二维数组的形式循环数据


    
      
        {{ item.data }}
      
    
  
数据结构的问题解决了,那么接下来就是如何判断数据模块是否超出可视范围

首先我们需要知道每个数据模块的高度,其实很简单,只需要为每个模块定义一个id,然后在数据展示之后根据id获取到该模块的节点信息然后按顺序存储到数组中即可

const pagesHeight = []
onReady(()=>{
    setPageHeight()
})

const upper = () => {
  ...
  nextTick(() => {
    // 每次获取新数据都调用一下
    setPageHeight();
  });
};

const setPageHeight = () => {
  let query = uni.createSelectorQuery();
  query
    .select(`#item-${currentShowPage.value}`)
    .boundingClientRect(res => {
      pagesHeight[currentShowPage.value] = res && res.height;
    })
    .exec();
};

OK,现在我们已经知道每个模块的高度了,然后就是监听模块与可视窗口的交叉范围。这里有两种方法,一种是JS获取可视窗口的高度与模块scrollTop进行差值计算,另一种是使用小程序的createIntersectionObserver方法让程序自行监听交叉区域

这里我展示的是第二种方法,如果对第一种方法感兴趣的朋友可以向上看第二章开头我推荐的《微信小程序虚拟列表》文章

关于createIntersectionObserver方法的使用其实很简单,我们只需要把可视窗口的id以及需要监听的模块id传入即可,详情看官方文档

onReady(() => {
  ...
  observer(currentShowPage.value);
});
const upper = () => {
  ...
  nextTick(() => {
    // 每次获取新数据都调用一下
    observer();
  });
};

// 允许渲染的数组下标,需要设置默认值
const visiblePagesList = ref([-1,0,1])
const observer = pageNum => {
  const observeView = wx
    .createIntersectionObserver()
    .relativeTo("#scroll", { top: 0, bottom: 0 });
  observeView.observe(`#item-${pageNum}`, res => {
    if (res.intersectionRatio > 0) visiblePagesList.value = [pageNum - 1, pageNum, pageNum + 1];
  });
};

最后就是在页面中判断该模块是否允许被渲染(也就是是否存储在visiblePagesList数组中),这里就很简单了,只需要写一个方法在页面中调用即可


    
      
      
    
  
const includePage = index => {
  return visiblePagesList.value.indexOf(index) > -1;
};

来看看效果如何

5.gif

额...似乎没有太大区别,那我们看看页面结构到底也没有将可视区域外的内容切换为空白view

6.gif

成功!

3、功能调整

聊天室原本还有回底功能等,也不能忘了加上

这个部分就比较简单了,只需要直接使用scroll-viewscroll-top属性,然后通过在scroll回调中动态记载scroll-top的值即可

下面是部分代码


  ...
  
  回底
let scrollTop;
const currentTop = ref(0);
const showGoBottom = ref(false);
const handle_scroll = throttle(event => {
  scrollTop = event[0].detail.scrollTop;
  if (scrollTop > 300) {
    showGoBottom.value = true;
  }
}, 100);
const handle_goBottom = () => {
  currentTop.value = scrollTop;
  nextTick(() => {
    currentTop.value = 0;
  });
  showGoBottom.value = false;
};

大功告成~

最后附上demo仓库

https://gitee.com/huang-qihao123/virtual-list-demo

推荐:《uniapp教程

相关专题

更多
高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

4

2026.01.16

全民K歌得高分教程大全
全民K歌得高分教程大全

本专题整合了全民K歌得高分技巧汇总,阅读专题下面的文章了解更多详细内容。

3

2026.01.16

C++ 单元测试与代码质量保障
C++ 单元测试与代码质量保障

本专题系统讲解 C++ 在单元测试与代码质量保障方面的实战方法,包括测试驱动开发理念、Google Test/Google Mock 的使用、测试用例设计、边界条件验证、持续集成中的自动化测试流程,以及常见代码质量问题的发现与修复。通过工程化示例,帮助开发者建立 可测试、可维护、高质量的 C++ 项目体系。

10

2026.01.16

java数据库连接教程大全
java数据库连接教程大全

本专题整合了java数据库连接相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.15

Java音频处理教程汇总
Java音频处理教程汇总

本专题整合了java音频处理教程大全,阅读专题下面的文章了解更多详细内容。

15

2026.01.15

windows查看wifi密码教程大全
windows查看wifi密码教程大全

本专题整合了windows查看wifi密码教程大全,阅读专题下面的文章了解更多详细内容。

42

2026.01.15

浏览器缓存清理方法汇总
浏览器缓存清理方法汇总

本专题整合了浏览器缓存清理教程汇总,阅读专题下面的文章了解更多详细内容。

7

2026.01.15

ps图片相关教程汇总
ps图片相关教程汇总

本专题整合了ps图片设置相关教程合集,阅读专题下面的文章了解更多详细内容。

9

2026.01.15

ppt一键生成相关合集
ppt一键生成相关合集

本专题整合了ppt一键生成相关教程汇总,阅读专题下面的的文章了解更多详细内容。

6

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 46.8万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号