主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情! |
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作 |
【相关学习推荐:微信小程序教程】
背景
在小程序中,你会遇到很多场景下的一长串交互。 如果页面渲染过多的wxml,使用node会导致小程序页面卡顿、白屏。 主要原因有:
1.列表数据量较大,初始化setData和绘制列表wxml需要时间。
2. 渲染了很多wxml节点。 每次setData更新视图时,都必须创建一个新的虚拟树,并且与旧树进行比较是相对耗时的。
3.需要渲染的wxml节点很多,页面只能容纳这么多wxml,占用大量内存。
微信小程序自带的滚动-视图没有针对长列表进行优化。 官方组件recycle-view是一个类似于virtual-list的长列表组件。 现在我们来分析一下虚拟列表的原理,并从头开始实现一个小程序虚拟-列表。
实现原理
首先你需要了解什么是virtual-list。 这是一个只加载“可见区域”及其附近dom元素的初始化,并且在滚动过程中仅重用dom元素。 滚动列表的前端优化技术,渲染“可见区域”及其附近的 DOM 元素。 与传统的列表方法相比,它可以实现非常高的初始渲染性能,并且仅在滚动期间保持超轻量级的 DOM 结构。
虚拟列表最重要的概念:
可滚动区域:例如列表容器的高度为600,其内部元素的高度之和为Exceed。 容器的这个区域可以滚动。 这是一个“可滚动区域”。
可见区域:例如一个列表容器,高度为600,上方有一个垂直滚动条。 右侧用于滚动。 视觉上可见的内部区域是“可见区域”。
虚拟列表实现的核心是监听滚动事件,通过滚动距离偏移和渲染来动态调整“可见区域”数据的顶部距离和前/后截距索引值。 滚动元素的总大小,totalSize。 实施步骤如下:
1. 监听滚动事件Top/scrollLeft,计算“显示区域”中起始项的索引值startIndex和结束项的索引值endIndex。
2.通过startIndex和endIndex截取长列表中“可见”的数据项。 更新列表。
3. 计算可滚动区域高度和项目偏移量并将其应用于可滚动区域和项目。
1. 列表项宽度/高度和滚动偏移
在虚拟列表中,“可滚动区域”是根据每个列表项的宽度/高度计算的,可能需要自定义。 定义 itemSizeGetter 函数来计算列表项的宽度/高度。
itemSizeGetter(itemSize) { return (index:number) => { if (isFunction(itemSize)) { return itemSize(index); Return isArray(itemSize) : ItemSize };
在滚动过程中,不计算不可见列表项的itemSize。 在这种情况下,使用估计的列表项estimateItemSize。 目的是在计算“可滚动区域”的高度时,计算出未测量的itemSize。 请改用estimatedItemSize。
getSizeAndPositionOfLastMeasuredItem() { return this.lastMeasuredIndex >= 0 ? this.itemSizeAndPositionData[this.lastMeasuredIndex] : { 偏移量: 0, 大小: 0 };t lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); return (lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size + (this.itemCount - this.lastMeasuredIndex - 1) * this.estimatedItemSize ); 复制代码
在此处显示发生了什么是最后计算的列表项的 itemSize 和 offset 直接通过缓存命中。 这是因为每个列表项的两个参数在检索时被缓存。
getSizeAndPositionForIndex(index:number) { if (index > this.lastMeasuredIndex) { const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); 让 offset = lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size for (let i = this.lastMeasuredIndex + 1; ; i <= 索引; i++) { const size = this.itemSizeGetter(i); this.itemSizeAndPositionData[i] = { 偏移量, 大小,}; 偏移量 += 大小; this.lastMeasuredIndex = 索引; }复制代码。 2.根据偏移量找到索引值。
滚动处理时必须传递滚动偏移offset。 计算显示区域中显示的第一个数据项的索引值。 通常,每个列表项的 itemSize 可以从 0 开始计算。 一旦累加超过偏移量,就可以得到索引值。 但如果数据量太大,频繁触发滚动事件,性能会明显下降。 幸运的是,列表项的滚动距离严格按照升序排列,因此我们可以对缓存的数据进行二分查找,将时间复杂度降低到 O(lgN) 。
js代码如下。
findNearestItem(offset:number) { offset = Math.max(0, offset); const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); const lastMeasuredIndex = Math.max(0 , this.lastMeasuredIndex); >= offset) { return this.binarySearch({ high: lastMeasuredIndex, low: 0, offset, }); } else { return this.exponentialSearch({ index: lastMeasuredIndex, offset, }); private binarySearch({ low, high, offset, }: { low: number; high: number; offset: number; }) { let middle = 0 } } currentOffset = 0; while (low <= high) { Medium = low + Math.floor((high - low) / 2); currentOffset = this.getSizeAndPositionForIndex(middle).offset; if (currentOffset === offset) { return middle Masu 。 } else if (currentOffset offset) {high = middle - 1; if (low > 0) { return low - 1; 返回 0 。 复制代码
对于不缓存计算结果的搜索,首先使用指数搜索来缩小搜索范围,然后使用二分搜索。
privateexponentialSearch({ 索引, 偏移量, }: { 索引: 数字; 偏移量: 数字; }) { 间隔 = 1; }while (index < this.itemCount && this.getSizeAndPositionForIndex(index).offset < offset ) { index += 间隔; 间隔 *= 2; return this.binarySearch({ high: Math.min(index, this.itemCount [k3 ] 1)、低:Math.floor(index / 2), offset, }); 复制代码。 3、计算startIndex、endIndex
调整“可视区域”大小、containerSize、滚动偏移、预渲染项数、overscanCount后,我们就可以计算出起始项的索引值startIndex。 以及“可见区域”的结束项的索引值endIndex。 实施步骤如下:
1. 找到距偏移量最近的索引值的距离。 该值为起始项startIndex的索引值。
2.通过startIndex获取item的偏移量和大小,并调整偏移量。
在3containerSize上加上.offset,得到最终item的maxOffset,从startIndex开始累加,直到超过maxOffset,得到最终item的索引值endIndex。
JS代码如下。
getVisibleRange({containerSize, offset, overscanCount, }: {containerSize: number; offset: number; overscanCount: number; }): { start?: number; stop?: number } { const maxOffset = offset + 容器大小; const datum = this.getSizeAndPositionForIndex(start); 。 偏移+基准尺寸;停止=让我们开始。 while (offset < maxOffset && stop < this.itemCount - 1) { stop++; offset += this.getSizeAndPositionForIndex(stop).size; if (overscanCount) { start = Math.max(0, start - overscanCount) ; stop = Math.min(stop + overscanCount, this.itemCount - 1); } return { start, stop, };} 复制代码 3. 监听滚动事件并实现虚拟列表滚动
现在可以实现通过侦听滚动事件并动态更新 startIndex、endIndex、totalSize 和 来实现虚拟列表滚动。 抵消。
js代码如下。
getItemStyle(index) { const style = this.styleCache[index];e) { return style; const {scrollDirection} = this.data; const { size, offset, } = this.sizeAndPositionManager.getSizeAndPositionForIndex(index); constcumputedStyle = styleToCssString({ 位置: '绝对', 顶部: 0, 左侧: 0 , width: '100%', [positionProp[scrollDirection]]: 偏移量, [sizeProp[scrollDirection]]: 大小, }); this.styleCache[index] =cumputedStyle ; },observScroll(offset:number){const{scrollDirection,overscanCount,visibleRange} = this.data; const { start, stop } = this.sizeAndPositionManager.getVisibleRange({ 容器大小: this.data[sizeProp[scrollDirection] ] || 0、偏移、超过canCount, }); const totalSize = this.sizeAndPositionManager.getTotalSize(); if (totalSize !== this.data.totalSize) { this.setData({totalSize }); if (visibleRange.start !== start ||visibleRange .stop !== stop) { const styleItems: string[] = []; if (isNumber(start) && isNumber(stop)) { let Index = start - 1 while (++index <= stop) { styleItems.push(this.getItemStyle(index)); this.triggerEvent('render', { startIndex: start, stopIndex: stop, styleItems, }); this.data.offset = offset; start; this.data.visibleRange.stop = stop; }, 复制代码
在调用时,我调用了 startIndex,通过渲染事件停止index、styleItems 截取长列表“显示区域”中的数据,并使用绝对定位将列表项的 itemSize 和偏移量应用到列表中。
代码如下。
let list = Array .from({ length: 10000 }).map((_,index) =>index);Page({ data: { itemSize:index => 50 * ((index % 3) + 1), styleItems: null, itemCount : list.length, list: [], }, onReady() { this.virtualListRef = this.virtualListRef || this.selectComponent('#virtual-list'); startIndex, stopIndex, styleItems } = e.detail; this.setData({ list: list.slice(startIndex, stopIndex + 1), styleItems, }), loadMore() { setTimeout(() => { const appendList = Array. from({ length: 10 }).map( (_, index) => list.length + index, ); list = list.concat(appendList);list.length, list: this.data.list.concat(appendList), }); },});复制代码 {{ item + 1 }} {{itemCount}}</view>复制代码参考资料
在为这个微信小程序创建virtual-list组件的过程中,我们主要参考了几个优秀的开源虚拟列表实现方案。
react-tiny-virtual-listreact-virtualizedreact-窗口概述
通过上面的讲解,我们首先在微信小程序环境下实现虚拟列表,我获得了一个对原理有更深入的理解。 列表。 但目前尚不适用于瀑布流布局或不可预测的列表项大小等场景。 在快速滚动期间,我会看到渲染速度太慢的白屏。 通过增加“可见区域”之外的预渲染项的数量,overscanCount,可以在一定程度上缓解这个问题。
下载微信应用
微信是一款移动通讯软件,支持通过手机网络发送语音消息、视频、照片和文本。 微信带来了全新的移动通信体验,您可以单独或群组聊天,还可以根据您的地理位置查找附近的人。 有需要的朋友,请快来保存您的下载体验吧!
这几篇文章你可能也喜欢:
- 了解微信小程序太郎的自动追踪点
- 当时微信小程序模仿了网易云音乐的实时搜索功能(微信小程序歌曲播放列表)。
- 实用---微信小程序Webpack工程考察(微信小程序开发工具需要哪些步骤?)
- 如何为微信小程序设置页面(如何为微信小程序设置页面背景)
- 为什么我开发微信小程序只使用upData?
本文由主机参考刊发,转载请注明:微信小程序中如何实现virtual-list详解(微信小程序视图类) https://zhujicankao.com/123213.html
评论前必须登录!
注册