mpvue icon indicating copy to clipboard operation
mpvue copied to clipboard

"无限"列表展示如何优化

Open rchunping opened this issue 6 years ago • 29 comments

列表中每一条是图文形式(类似朋友圈),比较复杂,数据也比较多,包括用户信息,文本信息,图片(多张)信息,评论信息等

放在<scroll-view>中v-for循环,里面用一个<message>组件负责展示和交互。

监听滑动触底事件每次加载20条,优化目标是展示1000条没有明显性能问题(之前有H5的版本,直接插入DOM的,没有太大性能问题)

目前遇到的问题,主要是安卓系统上碰到:

  1. 加载20条后,数据拿到,更新data后,ui更新明显延迟好几秒 (iPhone上秒更新)
  2. 加载到几百条后,明显感觉到卡顿,各种交互动画也开始跳帧 (iPhone上依然流畅,动画有轻微跳帧)

尝试的优化: 为了减少data上的数据集数量,获取每个展示节点的高度,并根据滚动位置,只显示5屏数据(当前屏+上下2屏)

<scroll-view>
  v-for : 
      <!-- 展示信息  -->
      <message v-if="visible">
      <view v-else class="placeholder" :style="{'height': height}" />
      <!-- 仅显示一个原message相同高度的空白占位 -->
</scroll-view>

优化后有一些改善,但是更新data到ui刷新 依然明显延迟 检查data里的数据,发现即使v-if 为false的那些组件的历史数据依然在整个data里面

请教各位有什么优化思路吗?

rchunping avatar Mar 26 '18 05:03 rchunping

赋值前可以对数据进行一次过滤,去掉不必要的条目和属性

F-loat avatar Mar 26 '18 05:03 F-loat

@F-loat 这个到不是关键 滑动的时候只有十几条数据在data上,问题是这十几条数据不会随着 v-if的变化而清掉,依然留在data.$root上

另一个问题是,v-for这种内部处理是不是有什么特别的地方,即使我的数据简单到一个数字数组,然后放在v-for里面展示, 当我追加20个数字到data的时候,ui刷新依然有明显延迟(安卓)。

rchunping avatar Mar 26 '18 05:03 rchunping

所以建议你通过数组的 filter 方法先把不想显示的数据去掉,比在模板里处理应该要高效一些,而且不会出问题,卡顿也可能是因为 v-if 造成的

F-loat avatar Mar 26 '18 05:03 F-loat

https://github.com/F-loat/ithome-lite 这个小程序的列表页数据几百条是不会有卡顿的情况的,当然可能和机型也有一定关系

F-loat avatar Mar 26 '18 05:03 F-loat

http://mpvue.com/mpvue/#_17

  • 最好不要在 v-for 里面嵌套组件
  • 同时那些 style 计算能避免就避免吧
  • 数据不要全挂在 data 上,比如可以用 new Vue 搞一个 eventHub 来集约数据,在 data 是只挂载要展示的数据

anchengjian avatar Mar 26 '18 05:03 anchengjian

@anchengjian 嗯,也看过这个指南。 产品就是不停刷不停翻的信息流,现在是在各种测试从wepy迁移过来的问题

我试试看能不能v-for里面引入小程序自己的自定义组件来减少data上循环组件数据

rchunping avatar Mar 26 '18 06:03 rchunping

@rchunping 同,一样遇到这个问题,请问你现在有什么好的方案吗

fangdiao avatar Apr 19 '18 13:04 fangdiao

@fangdiao 什么问题?UI更新慢还是数据太多卡?

rchunping avatar Apr 19 '18 13:04 rchunping

@rchunping 我也是无限长列表,数据多就卡,ui延迟。一直找不到好的方案优化

fangdiao avatar Apr 19 '18 13:04 fangdiao

@fangdiao 我们这边测试下来,ui延迟在iOS上完全没问题,安卓上暂时无解(不过真机上效果也能接受)

大列表导致ui卡顿的问题,我们的解决方案是:

  1. 如果列表的展示卡片UI比较简单,那么不要用组件。同时控制data数据量,只传必要的一部分数据。 比如不管列表多长,始终传入够展示5屏的数据量(当前屏和上下各2屏,根据实际效果调整),其他列表项用0填充。 比如 this.list = [0,0,0,0,0,.......,{数据n},{数据n+1},{数据n+2},.....,0,0,0,0] 模板v-for中用v-if 进行开关:碰到0就显示一个空白的<view>占位,非0就展示实际的卡片内容 (不要用v-show,否则会提前碰到DOM节点超出数量限制的错误) 然后根据scroll-view的滚屏事件动态计算列表中哪些位置需要填充真实的数据,更新this.list进行UI渲染。

卡片定高的这样做就行了,如果卡片不定高而且最终高度无法通过数据进行精确计算的话,还需要动态检测每个卡片的实际高度,并把数组中的0用实际的高度代替。这个做起来复杂一点,比如未知高度的卡片第一次始终需要先设置进数据展示一次才能拿到高度之类的.....

  1. 如果列表卡片UI很复杂,那么也不要用Vue的组件,因为v-for内的Vue的组件会导致数据倍增(this.list上一份,然后每个卡片组件又一份,组件用过的data并不会随着v-if的开关而减少)

我们的经验是把卡片做成原生的小程序自定义组件,同时 this.list 也要按照上面的方法只传入必要那几个当前需要展示的卡片数据,其他用0代替

我们项目中上面2中情况都有使用,实际测试下来,刷几千条都不会有卡顿问题。

当然实际上很多地方都需要微调优化,scroll检测频率,this.list更新频率都会影响实际效果。

rchunping avatar Apr 19 '18 16:04 rchunping

感谢分享,十分感谢!不知道为什么没给我邮件提醒,所以回复慢了些,见谅。 我的每个卡片都有不少判断,比较复杂,但是高度是定了的。我没有做成组件在v for中循环,而是将数据传入一个组件,在一个组件内循环。感觉跟你的用小程序原生组件写有些相似。 还有一点是请问你使用了vuex吗,现在我的list都放在state中,如果要做到list只存放部分数据且不放在vue中,那么得把数据拿出来放在vue外面,而且我在mutation中还有一些对list的操作的逻辑,那么这些逻辑都得拿出来放在外面。感觉有些麻烦。请问你们是怎么处理的,谢谢。

fangdiao avatar Apr 20 '18 06:04 fangdiao

@fangdiao 不用vuex维护大列表,单独或者几个页面共享一个大的Object维护获取到的数据。减少data上的传值。你用vuex引入计算属性的话最终还是在data上

rchunping avatar Apr 20 '18 06:04 rchunping

谢谢。 是这样的,冗余数据放在vue中都会影响性能,请问你们是单独的一个js文件存放冗余数据,再export出来吗

fangdiao avatar Apr 20 '18 06:04 fangdiao

@fangdiao 你要几个页面共用或者需要跨组件操作就放单独js里导出来用

rchunping avatar Apr 20 '18 06:04 rchunping

好的,谢谢答疑解惑

fangdiao avatar Apr 20 '18 07:04 fangdiao

列表不能用 cell, slot 又不能用在 list 里, filter 又不能用,这实在是不太方便

exherb avatar Apr 28 '18 10:04 exherb

mark一下

Rem486 avatar Aug 21 '18 01:08 Rem486

@rchunping 解决方案很不错。不过我有两个疑问:

  1. 你们是如何在mpvue中使用小程序的原生组件的
  2. 占位view如果不设置高度,会有什么问题呢

Youjingyu avatar Sep 27 '18 01:09 Youjingyu

@rchunping 按照你的思路实现了一版,现在的问题是在快速滑动的时候,比如用户非常快地就滑动了5屏,视图来不及更新,导致用户会看到空白的占位view,不知道你们是怎么解决的?

Youjingyu avatar Sep 29 '18 02:09 Youjingyu

有哪个大佬解决了吗?安卓机真的承受不住啊

0415ypt00 avatar Oct 24 '18 06:10 0415ypt00

@Youjingyu 设置一张默认的占位图吧,或者加个简单的loading效果,看到图片/loading总比看到空白的view强

ExcellentJR avatar Nov 26 '18 07:11 ExcellentJR

@fangdiao 我们这边测试下来,ui延迟在iOS上完全没问题,安卓上暂时无解(不过真机上效果也能接受)

大列表导致ui卡顿的问题,我们的解决方案是:

  1. 如果列表的展示卡片UI比较简单,那么不要用组件。同时控制data数据量,只传必要的一部分数据。 比如不管列表多长,始终传入够展示5屏的数据量(当前屏和上下各2屏,根据实际效果调整),其他列表项用0填充。 比如 this.list = [0,0,0,0,0,.......,{数据n},{数据n+1},{数据n+2},.....,0,0,0,0] 模板v-for中用v-if 进行开关:碰到0就显示一个空白的占位,非0就展示实际的卡片内容 (不要用v-show,否则会提前碰到DOM节点超出数量限制的错误) 然后根据scroll-view的滚屏事件动态计算列表中哪些位置需要填充真实的数据,更新this.list进行UI渲染。

卡片定高的这样做就行了,如果卡片不定高而且最终高度无法通过数据进行精确计算的话,还需要动态检测每个卡片的实际高度,并把数组中的0用实际的高度代替。这个做起来复杂一点,比如未知高度的卡片第一次始终需要先设置进数据展示一次才能拿到高度之类的.....

  1. 如果列表卡片UI很复杂,那么也不要用Vue的组件,因为v-for内的Vue的组件会导致数据倍增(this.list上一份,然后每个卡片组件又一份,组件用过的data并不会随着v-if的开关而减少)

我们的经验是把卡片做成原生的小程序自定义组件,同时 this.list 也要按照上面的方法只传入必要那几个当前需要展示的卡片数据,其他用0代替

我们项目中上面2中情况都有使用,实际测试下来,刷几千条都不会有卡顿问题。

当然实际上很多地方都需要微调优化,scroll检测频率,this.list更新频率都会影响实际效果。

借用了你的思路,不过我的需求列表卡片是不定高的,计算高度的成本会随列表项的数量递增,没想到具体解决思路

Elliott-Hu avatar Feb 28 '19 02:02 Elliott-Hu

@Elliott-Hu 我这边的思路目前是用数组存储每个卡片的高度加上之前所有卡片的高度。

card[0] = card[0].height
card[1] = card[1].height + card[0]
card[2] = card[2].height + card[1]
...

这样用二分法来查找scrollTop对应的是哪个卡片。不知是否有更好的方案?

michaelwnyc avatar Mar 22 '19 04:03 michaelwnyc

mark

liulinboyi avatar Aug 21 '19 08:08 liulinboyi

mark,期待更好的方案

hoelshen avatar Dec 25 '19 08:12 hoelshen

@fangdiao 我们这边测试下来,ui延迟在iOS上完全没问题,安卓上暂时无解(不过真机上效果也能接受)

大列表导致ui卡顿的问题,我们的解决方案是:

  1. 如果列表的展示卡片UI比较简单,那么不要用组件。同时控制data数据量,只传必要的一部分数据。 比如不管列表多长,始终传入够展示5屏的数据量(当前屏和上下各2屏,根据实际效果调整),其他列表项用0填充。 比如 this.list = [0,0,0,0,0,.......,{数据n},{数据n+1},{数据n+2},.....,0,0,0,0] 模板v-for中用v-if 进行开关:碰到0就显示一个空白的占位,非0就展示实际的卡片内容 (不要用v-show,否则会提前碰到DOM节点超出数量限制的错误) 然后根据scroll-view的滚屏事件动态计算列表中哪些位置需要填充真实的数据,更新this.list进行UI渲染。

卡片定高的这样做就行了,如果卡片不定高而且最终高度无法通过数据进行精确计算的话,还需要动态检测每个卡片的实际高度,并把数组中的0用实际的高度代替。这个做起来复杂一点,比如未知高度的卡片第一次始终需要先设置进数据展示一次才能拿到高度之类的.....

  1. 如果列表卡片UI很复杂,那么也不要用Vue的组件,因为v-for内的Vue的组件会导致数据倍增(this.list上一份,然后每个卡片组件又一份,组件用过的data并不会随着v-if的开关而减少)

我们的经验是把卡片做成原生的小程序自定义组件,同时 this.list 也要按照上面的方法只传入必要那几个当前需要展示的卡片数据,其他用0代替

我们项目中上面2中情况都有使用,实际测试下来,刷几千条都不会有卡顿问题。

当然实际上很多地方都需要微调优化,scroll检测频率,this.list更新频率都会影响实际效果。

scroll检测频率这个检测频率怎么控制呢?需要知道什么时候滚动停止了吗?

feliciaZH avatar Dec 27 '19 06:12 feliciaZH

mark

linyining2013 avatar Nov 10 '20 03:11 linyining2013

提供一个成本最低,性能更好方案:《京喜小程序的高性能打造之路》大图、长列表优化 利用 IntersectionObserver ,比直接监听 scroll 事件性能更好。

proYang avatar Nov 10 '20 03:11 proYang

@fangdiao 我们这边测试下来,ui延迟在iOS上完全没问题,安卓上暂时无解(不过真机上效果也能接受)

大列表导致ui卡顿的问题,我们的解决方案是:

  1. 如果列表的展示卡片UI比较简单,那么不要用组件。同时控制data数据量,只传必要的一部分数据。 比如不管列表多长,始终传入够展示5屏的数据量(当前屏和上下各2屏,根据实际效果调整),其他列表项用0填充。 比如 this.list = [0,0,0,0,0,.......,{数据n},{数据n+1},{数据n+2},.....,0,0,0,0] 模板v-for中用v-if 进行开关:碰到0就显示一个空白的占位,非0就展示实际的卡片内容 (不要用v-show,否则会提前碰到DOM节点超出数量限制的错误) 然后根据scroll-view的滚屏事件动态计算列表中哪些位置需要填充真实的数据,更新this.list进行UI渲染。

卡片定高的这样做就行了,如果卡片不定高而且最终高度无法通过数据进行精确计算的话,还需要动态检测每个卡片的实际高度,并把数组中的0用实际的高度代替。这个做起来复杂一点,比如未知高度的卡片第一次始终需要先设置进数据展示一次才能拿到高度之类的.....

  1. 如果列表卡片UI很复杂,那么也不要用Vue的组件,因为v-for内的Vue的组件会导致数据倍增(this.list上一份,然后每个卡片组件又一份,组件用过的data并不会随着v-if的开关而减少)

我们的经验是把卡片做成原生的小程序自定义组件,同时 this.list 也要按照上面的方法只传入必要那几个当前需要展示的卡片数据,其他用0代替

我们项目中上面2中情况都有使用,实际测试下来,刷几千条都不会有卡顿问题。

当然实际上很多地方都需要微调优化,scroll检测频率,this.list更新频率都会影响实际效果。

最近我也遇到这个问题,方法理论上不错,先实现看看。

ghost avatar Apr 13 '21 11:04 ghost