提交 8a973878 编写于 作者: 雪洛's avatar 雪洛

feat: 优化部分场景下自行实现长列表的性能

上级 010e98f7
## uni-recycle-view【TODO 名称待定,由于使用uni-开头应避免和内置组件冲突,比如uni-list和list组件是冲突的】
::: tip 组件名:uni-recycle-view
> 代码块: `uRecycleView`、`uRecycleItem`
【TODO 链接待补充】
[点击下载&安装]()
:::
uni-recycle-view 组件用于在展示超长列表时优化内存占用以及性能。不同于uni-app list-view组件,uni-recycle-view组件不会对所有数据循环创建VNode。适用于仅使用一个for循环创建所有列表项的场景。
### 基本用法
```vue
<template>
<uni-recycle-view style="flex: 1;" :list="list">
<template v-slot:default="{items}">
<uni-recycle-item class="item" v-for="item in (items as Item[])" :item="item" :key="item.id">
<view class="item-wrapper">
<view class="name"><text style="font-size: 14px;">{{item.name}}</text></view>
<view class="info"><text style="font-size: 12px; color: #999999;">{{item.info}}</text></view>
</view>
</uni-recycle-item>
</template>
</uni-recycle-view>
</template>
<script>
type Item = {
id : number
name : string
info : string
}
export default {
data() {
return {
list: [] as Item[]
}
},
created() {
for (let i = 0; i < 2000; i++) {
this.list.push({
id: i,
name: `Wifi_` + i,
info: `信号强度: -${(Math.floor(Math.random() * 60) + 40)} db, 安全性: WPA/WPA2/WPA3-Personal`
} as Item)
}
}
}
</script>
```
uni-recycle-view组件传入通过绑定list属性`:list="list"`传入含所有数据的列表。经过组件内部计算,由作用域插槽返回真实要渲染的部分数据`v-slot:default="{items}"`。最终仅需渲染items而不是list,从而节省了大量的计算消耗及内存占用。
### 属性及事件
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|list |any[]| [] |列表所有数据 |
|其他 |- |- |其余属性及事件会透传给内部的scroll-view组件,参考: [scroll-view组件](https://doc.dcloud.net.cn/uni-app-x/component/scroll-view.html) |
## uni-recycle-item
uni-recycle-item用于渲染uni-recycle-view筛选出的用于展示的数据。
### 属性及事件
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|item |any | - |当前组件渲染的列表项 |
## 注意事项
- uni-recycle-view和uni-recycle-item必须搭配使用
- uni-recycle-view仅渲染滚动容器当前屏及上下5屏的内容
## 已知问题
- 不支持设置初始滚动位置
- 容器大小变动时未刷新缓存的子元素大小,可能导致滚动过程中出现跳动
- 列表数据内每一项不可以是基础类型
- uni-recycle-item不要设置margin,会影响滚动位置的计算
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
hasDefaultSize: false, hasDefaultSize: false,
defaultItemSize: 40, defaultItemSize: 40,
lastScrollTop: 0, lastScrollTop: 0,
rearrangeQueue: [] as number[]
}; };
}, },
provide() { provide() {
...@@ -84,9 +85,29 @@ ...@@ -84,9 +85,29 @@
const scrollTop = e.detail.scrollTop const scrollTop = e.detail.scrollTop
this.lastScrollTop = 0 this.lastScrollTop = 0
if (scrollTop < this.offsetThreshold[1] || scrollTop > this.offsetThreshold[2]) { if (scrollTop < this.offsetThreshold[1] || scrollTop > this.offsetThreshold[2]) {
this.rearrange(scrollTop) this.queue(scrollTop)
} }
}, },
queue(scrollTop : number) {
/*
* rearrange内为大量同步逻辑,在上次rearrange未执行完毕的情况下将后续多个rearrange合并成一次执行,即仅执行最后一次
* 由于滚动机制差异,此优化仅在web端才有意义。
* 如何测试:push后console.log(this.rearrangeQueue.length) 输出结果大于1时触发优化
*/
this.rearrangeQueue.push(scrollTop)
setTimeout(() => {
this.flush()
}, 1)
},
flush() {
const queueLength = this.rearrangeQueue.length
if (queueLength === 0) {
return
}
const lastScrollTop = this.rearrangeQueue[queueLength - 1]
this.rearrange(lastScrollTop)
this.rearrangeQueue = [] as number[]
},
rearrange(scrollTop : number) { rearrange(scrollTop : number) {
this.offsetThreshold[0] = Math.max(scrollTop - this.scrollElementHeight * 5, 0) this.offsetThreshold[0] = Math.max(scrollTop - this.scrollElementHeight * 5, 0)
this.offsetThreshold[1] = Math.max(scrollTop - this.scrollElementHeight * 3, 0) this.offsetThreshold[1] = Math.max(scrollTop - this.scrollElementHeight * 3, 0)
...@@ -119,4 +140,4 @@ ...@@ -119,4 +140,4 @@
<style> <style>
</style> </style>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册