【Verwandte Lernempfehlungen: WeChat Mini-Programm-Tutorial】
Das Miniprogramm wird in vielen Szenarien auf lange Listen von Interaktionen stoßen, wenn eine Seite zu viele WXML-Knoten rendert, was dazu führt Verzögerung und weißer Bildschirm auf der Miniprogrammseite. Die Hauptgründe sind wie folgt:
1. Die Menge der Listendaten ist groß und es dauert lange, setData zu initialisieren und die Rendering-Liste wxml zu initialisieren.
2 Der virtuelle Baum und die Diff-Operation des alten Baums müssen jedes Mal erstellt werden.
3 Es werden viele WXML-Knoten gerendert Der belegte Speicher ist hoch.
Die Scroll-Ansicht des WeChat-Applets selbst ist nicht für lange Listen optimiert. Die offizielle Recycling-Ansicht der Komponente ist eine lange Listenkomponente, die der virtuellen Liste ähnelt. Jetzt analysieren wir das Prinzip der virtuellen Liste und implementieren eine virtuelle Liste eines kleinen Programms von Grund auf.
Zunächst müssen wir verstehen, was eine virtuelle Liste ist. Dies ist eine Initialisierung, die nur den „sichtbaren Bereich“ und seine nahegelegenen Dom-Elemente lädt und den „sichtbaren Bereich“ nur durch Wiederverwendung von Dom-Elementen rendert während des Scrollvorgangs“ und der Scrolling-Listen-Frontend-Optimierungstechnologie der nahegelegenen Dom-Elemente. Im Vergleich zur herkömmlichen Listenmethode kann eine extrem hohe anfängliche Rendering-Leistung erzielt werden und während des Bildlaufvorgangs wird nur eine ultraleichte DOM-Struktur beibehalten.
Die wichtigsten Konzepte virtueller Listen:
Scrollbarer Bereich: Wenn beispielsweise die Höhe des Listencontainers 600 beträgt und die Summe der Höhen der internen Elemente die Höhe des Containers überschreitet, kann dieser Bereich sein gescrollt, was der „scrollbare Bereich“ ist der „visuelle Bereich“.
Der Kern der Implementierung einer virtuellen Liste besteht darin, das Scroll-Ereignis abzuhören und den oberen Abstand sowie die vorderen und hinteren Abfangindexwerte der Datenwiedergabe des „visuellen Bereichs“ durch den Scroll-Distanz-Offset und die dynamische Anpassung anzupassen Summe der Größen der gescrollten Elemente, totalSize. Die Implementierungsschritte sind wie folgt:
getSizeAndPositionForIndex(index: number) { if (index > this.lastMeasuredIndex) { const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); let offset = lastMeasuredSizeAndPosition.offset + lastMeasuredSizeAndPosition.size; for (let i = this.lastMeasuredIndex + 1; i <= index; i++) { const size = this.itemSizeGetter(i); this.itemSizeAndPositionData[i] = { offset, size, }; offset += size; } this.lastMeasuredIndex = index; } return this.itemSizeAndPositionData[index]; }复制代码
Während des Scrollvorgangs müssen Sie den Indexwert der ersten im „sichtbaren Bereich“ angezeigten Daten über den Scroll-Offset-Offset berechnen 0. Die itemSize jedes Listenelements wird akkumuliert, bis sie den Offset überschreitet, und der Indexwert kann abgerufen werden. Wenn jedoch die Datenmenge zu groß ist und Scroll-Ereignisse häufig ausgelöst werden, kommt es zu einem großen Leistungsverlust. Glücklicherweise ist die Scrolldistanz der Listenelemente vollständig in aufsteigender Reihenfolge angeordnet, sodass Sie eine binäre Suche in den zwischengespeicherten Daten durchführen können, wodurch die Zeitkomplexität auf O(lgN) reduziert wird.
Der js-Code lautet wie folgt:
findNearestItem(offset: number) { offset = Math.max(0, offset); const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem(); const lastMeasuredIndex = Math.max(0, this.lastMeasuredIndex); if (lastMeasuredSizeAndPosition.offset >= 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; let currentOffset = 0; while (low <= high) { middle = low + Math.floor((high - low) / 2); currentOffset = this.getSizeAndPositionForIndex(middle).offset; if (currentOffset === offset) { return middle; } else if (currentOffset < offset) { low = middle + 1; } else if (currentOffset > offset) { high = middle - 1; } } if (low > 0) { return low - 1; } return 0; }复制代码
Für die Suche ohne zwischengespeicherte Berechnungsergebnisse verwenden Sie zunächst die exponentielle Suche, um den Suchbereich einzugrenzen, und verwenden Sie dann die binäre Suche.
private exponentialSearch({ index, offset, }: { index: number; offset: number; }) { let interval = 1; while ( index < this.itemCount && this.getSizeAndPositionForIndex(index).offset < offset ) { index += interval; interval *= 2; } return this.binarySearch({ high: Math.min(index, this.itemCount - 1), low: Math.floor(index / 2), offset, }); } }复制代码
Wir kennen die Größe des „sichtbaren Bereichs“ und den rollenden Offset-Offset. Nachdem wir die Anzahl der vorgerenderten Balken overscanCount angepasst haben, können wir den Beginn des „sichtbaren Bereichs“ berechnen. Der Indexwert startIndex des Startelements und der Indexwert endIndex des Endelements lauten wie folgt:
1 Finden Sie den Indexwert, der dem Offset am nächsten kommt. Dieser Wert ist der Indexwert startIndex des Startelements
2. Ermitteln Sie den Indexwert dieses Elements über den Offset und die Größe von startIndex und passen Sie dann den Offset an Endelementindexwert endIndex.
getVisibleRange({ containerSize, offset, overscanCount, }: { containerSize: number; offset: number; overscanCount: number; }): { start?: number; stop?: number } { const maxOffset = offset + containerSize; let start = this.findNearestItem(offset); const datum = this.getSizeAndPositionForIndex(start); offset = datum.offset + datum.size; let stop = 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. Hören Sie sich das Scroll-Ereignis an, um einen virtuellen Listen-Scroll zu realisieren.
Jetzt können Sie einen virtuellen Listen-Scroll realisieren, indem Sie auf das Scroll-Ereignis hören und startIndex, endIndex, totalSize und offset dynamisch aktualisieren . Der
js-Code lautet wie folgt:
getItemStyle(index) { const style = this.styleCache[index]; if (style) { return style; } const { scrollDirection } = this.data; const { size, offset, } = this.sizeAndPositionManager.getSizeAndPositionForIndex(index); const cumputedStyle = styleToCssString({ position: 'absolute', top: 0, left: 0, width: '100%', [positionProp[scrollDirection]]: offset, [sizeProp[scrollDirection]]: size, }); this.styleCache[index] = cumputedStyle; return cumputedStyle; }, observeScroll(offset: number) { const { scrollDirection, overscanCount, visibleRange } = this.data; const { start, stop } = this.sizeAndPositionManager.getVisibleRange({ containerSize: this.data[sizeProp[scrollDirection]] || 0, offset, overscanCount, }); 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; this.data.visibleRange.start = start; this.data.visibleRange.stop = stop; },复制代码
在调用的时候,通过render事件回调出来的startIndex, stopIndex,styleItems,截取长列表「可视区域」的数据,在把列表项目的itemSize和offset通过绝对定位的方式应用在列表上
代码如下:
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'); }, slice(e) { const { 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); this.setData({ itemCount: list.length, list: this.data.list.concat(appendList), }); }, 500); }, });复制代码
<view class="container"> <virtual-list scrollToIndex="{{ 16 }}" lowerThreshold="{{50}}" height="{{ 600 }}" overscanCount="{{10}}" item-count="{{ itemCount }}" itemSize="{{ itemSize }}" estimatedItemSize="{{100}}" bind:render="slice" bind:scrolltolower="loadMore"> <view wx:if="{{styleItems}}"> <view wx:for="{{ list }}" wx:key="index" style="{{ styleItems[index] }};line-height:50px;border-bottom:1rpx solid #ccc;padding-left:30rpx">{{ item + 1 }}</view> </view> </virtual-list> {{itemCount}}</view>复制代码
在写这个微信小程序的virtual-list组件过程中,主要参考了一些优秀的开源虚拟列表实现方案:
通过上述解释已经初步实现了在微信小程序环境中实现了虚拟列表,并且对虚拟列表的原理有了更加深入的了解。但是对于瀑布流布局,列表项尺寸不可预测等场景依然无法适用。在快速滚动过程中,依然会出现来不及渲染而白屏,这个问题可以通过增加「可视区域」外预渲染的item条数overscanCount来得到一定的缓解。
想了解更多编程学习,敬请关注php培训栏目!
Das obige ist der detaillierte Inhalt vonAusführliche Erklärung zur Implementierung einer virtuellen Liste im WeChat-Applet. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!