目次
背景
実装原理
参考资料
总结
ホームページ WeChat アプレット ミニプログラム開発 WeChat アプレットに仮想リストを実装する方法の詳細な説明

WeChat アプレットに仮想リストを実装する方法の詳細な説明

Sep 07, 2020 pm 03:48 PM
WeChat アプレット

WeChat アプレットに仮想リストを実装する方法の詳細な説明

【関連する学習の推奨事項: WeChat ミニ プログラム チュートリアル ]

背景

ミニ プログラムでは、多くのシナリオでインタラクションの長いリストが発生します。ページでレンダリングされる wxml ノードが多すぎると、ミニ プログラム ページがフリーズして画面が白くなります。主な理由としては、

1. リストデータ量が多く、setDataの初期化やレンダリングリストwxmlの初期化に時間がかかる、

2. が多いwxml ノードがレンダリングされ、そのたびに SetData がビューを更新するために新しい仮想ツリーを作成する必要があり、古いツリーの差分操作には比較的時間がかかります;

3. レンダリングされる wxml ノードが多数あります。ページに収容できる wxml は限られており、メモリ占有量は多くなります。

WeChat アプレット自体のスクロールビューは長いリスト用に最適化されていないため、公式コンポーネントの recycle-view は virtual-list に似た長いリスト コンポーネントです。ここで、仮想リストの原理を分析し、小さなプログラムの仮想リストを最初から実装してみます。

実装原理

まず第一に、virtual-list とは何かを理解する必要があります。これは、「可視領域」とその近くの dom 要素のみをロードし、実行中にそれらを再利用する初期化です。スクロール プロセス: 「表示領域」とその近くの DOM 要素のみをレンダリングするスクロール リストのフロントエンド最適化テクノロジ。従来のリスト方式と比較して、非常に高い初期レンダリング パフォーマンスを実現でき、スクロール処理中のみ超軽量の DOM 構造を維持します。

仮想リストの最も重要な概念:

  • スクロール可能な領域: たとえば、リスト コンテナーの高さは 600 で、内部コンテナーの高さの合計は 600 です。要素がコンテナの高さを超えています。この領域はスクロールできます。これは「スクロール可能領域」です。

  • 可視領域: たとえば、リスト コンテナの高さは 600 で、右側にはスクロール用の垂直スクロール バーがあり、視覚的に表示されます。内部領域は「視覚領域」です。

仮想リストの実装の核心は、スクロール イベントをリッスンし、スクロール距離オフセットを通じて上部の距離と「視覚領域」データ レンダリングの前後のインターセプトを動的に調整することです。およびスクロールされた要素のサイズの合計 totalSize インデックス値、実装手順は次のとおりです:

1. スクロール イベントのscrollTop/scrollLeftをリッスンし、スクロール イベントのインデックス値startIndexを計算します。 「可視領域」の開始項目と終了項目のインデックス値 endIndex;

2 .startIndex と endIndex を通じて長いリストの「可視領域」のデータ項目をインターセプトし、それらをlist;

3. スクロール可能領域の高さと項目のオフセットを計算し、スクロール可能領域と項目に適用します。

WeChat アプレットに仮想リストを実装する方法の詳細な説明
#1. リスト項目の幅/高さおよびスクロール オフセット

仮想リストでは、各リストによって異なります。 item 幅/高さは「スクロール可能領域」の計算に使用され、カスタマイズが必要になる場合があります。リスト項目の幅/高さを計算するには itemSizeGetter 関数を定義します。

itemSizeGetter(itemSize) {      return (index: number) => {        if (isFunction(itemSize)) {          return itemSize(index);
        }        return isArray(itemSize) ? itemSize[index] : itemSize;
      };
    }复制代码
ログイン後にコピー

スクロール処理中、表示されていないリスト項目の itemSize は計算されません。このとき、推定リスト項目estimatedItemSizeが使用されます。目的は、「」の高さを計算することです。渡された itemSize は、estimatedItemSize に置き換えられます。

getSizeAndPositionOfLastMeasuredItem() {    return this.lastMeasuredIndex >= 0
      ? this.itemSizeAndPositionData[this.lastMeasuredIndex]
      : { offset: 0, size: 0 };
  }

getTotalSize(): number {    const lastMeasuredSizeAndPosition = this.getSizeAndPositionOfLastMeasuredItem();    return (
      lastMeasuredSizeAndPosition.offset +
      lastMeasuredSizeAndPosition.size +
      (this.itemCount - this.lastMeasuredIndex - 1) * this.estimatedItemSize
    );
  }复制代码
ログイン後にコピー

ここでは、最後に計算されたリスト項目の itemSize と offset がキャッシュを通じて直接ヒットしていることがわかります。これは、各リスト項目の 2 つのパラメーターが取得されるときに、それらがキャッシュされるためです。

 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];
 }复制代码
ログイン後にコピー

2. オフセットに基づいたインデックス値の検索

スクロール処理中、スクロールを通じて「表示領域」に表示される最初のデータのインデックス値を計算する必要があります。 offset offset. 通常、各リスト項目の itemSize は 0 から計算され、offset を超えるとインデックス値が取得されます。ただし、データ量が多すぎてスクロール イベントが頻繁にトリガーされると、パフォーマンスが大幅に低下します。幸いなことに、リスト項目のスクロール距離は完全に昇順に配置されているため、キャッシュされたデータに対して二分検索を実行して、時間の複雑さを O(lgN) に減らすことができます。

js コードは次のとおりです。

  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;
  }复制代码
ログイン後にコピー

計算結果をキャッシュしない検索の場合は、まず指数関数検索で検索範囲を絞り、次に二分検索を使用します。

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,
    });
  }
}复制代码
ログイン後にコピー

3. startIndex と endIndex を計算します

「視覚領域」containerSize、ローリング オフセット offset のサイズがわかっているので、事前にレンダリングされたバーの数 overscanCount を追加して調整します。 「可視領域」の開始項目のインデックス値 startIndex と終了項目のインデックス値 endIndex を計算します。実装手順は次のとおりです:

1. オフセットに最も近いインデックス値を見つけます。この値開始項目のインデックス値です。startIndex;

2. startIndex を通じて項目のオフセットとサイズを取得し、オフセットを調整します。

3. オフセットをcontainerSizeに追加して、終了項目の maxOffset を取得し、startIndex から maxOffset を超えて終了項目のインデックス値 endIndex を取得するまで累積を開始します。

js コードは次のとおりです:

 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. 仮想リストのスクロールを実現するためにスクロール イベントをリッスンします

startIndex、endIndex、totalSize、およびを動的に更新できるようになりました。スクロールイベントをリッスンすることでオフセットを取得し、仮想リストスクロールを実現できます。

js コードは次のとおりです:

  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: &#39;absolute&#39;,        top: 0,        left: 0,        width: &#39;100%&#39;,
        [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(&#39;render&#39;, {          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(&#39;#virtual-list&#39;);
  },

  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>复制代码
ログイン後にコピー
WeChat アプレットに仮想リストを実装する方法の詳細な説明

参考资料

在写这个微信小程序的virtual-list组件过程中,主要参考了一些优秀的开源虚拟列表实现方案:

  • react-tiny-virtual-list
  • react-virtualized
  • react-window

总结

通过上述解释已经初步实现了在微信小程序环境中实现了虚拟列表,并且对虚拟列表的原理有了更加深入的了解。但是对于瀑布流布局,列表项尺寸不可预测等场景依然无法适用。在快速滚动过程中,依然会出现来不及渲染而白屏,这个问题可以通过增加「可视区域」外预渲染的item条数overscanCount来得到一定的缓解。

想了解更多编程学习,敬请关注php培训栏目!

以上がWeChat アプレットに仮想リストを実装する方法の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Xianyu WeChat ミニプログラムが正式に開始 Xianyu WeChat ミニプログラムが正式に開始 Feb 10, 2024 pm 10:39 PM

Xianyu の公式 WeChat ミニ プログラムが静かに開始されました。ミニ プログラムでは、プライベート メッセージを投稿して購入者/販売者とコミュニケーションしたり、個人情報や注文を表示したり、商品を検索したりすることができます。プログラム、見てみましょう。 Xianyu WeChat アプレットの名前は何ですか? 回答: Xianyu、アイドル取引、中古品販売、評価、リサイクル。 1. ミニ プログラムでは、アイドル メッセージの投稿、プライベート メッセージを介した購入者/販売者とのコミュニケーション、個人情報と注文の表示、指定された商品の検索などができます。 2. ミニ プログラム ページには、ホームページ、近くに、アイドル投稿、メッセージ投稿、私の投稿 5つの機能; 3. 使用したい場合は、購入する前に WeChat 支払いを有効にする必要があります。

WeChatアプレットは画像アップロード機能を実装 WeChatアプレットは画像アップロード機能を実装 Nov 21, 2023 am 09:08 AM

WeChat アプレットが画像アップロード機能を実装 モバイル インターネットの発展に伴い、WeChat アプレットは人々の生活に欠かせないものになりました。 WeChat ミニ プログラムは、豊富なアプリケーション シナリオを提供するだけでなく、画像アップロード機能などの開発者定義の機能もサポートします。この記事では、WeChat アプレットに画像アップロード機能を実装する方法と具体的なコード例を紹介します。 1. 準備作業 コードを書き始める前に、WeChat 開発者ツールをダウンロードしてインストールし、WeChat 開発者として登録する必要があります。同時に、WeChat についても理解する必要があります。

WeChat アプレットにドロップダウン メニュー効果を実装する WeChat アプレットにドロップダウン メニュー効果を実装する Nov 21, 2023 pm 03:03 PM

WeChat ミニ プログラムでドロップダウン メニュー効果を実装するには、特定のコード サンプルが必要です。モバイル インターネットの普及に伴い、WeChat ミニ プログラムはインターネット開発の重要な部分となり、ますます多くの人が注目し始めています。 WeChat ミニ プログラムを使用します。 WeChat ミニ プログラムの開発は、従来の APP 開発よりも簡単かつ迅速ですが、特定の開発スキルを習得する必要もあります。 WeChat ミニ プログラムの開発では、ドロップダウン メニューが一般的な UI コンポーネントであり、より良いユーザー エクスペリエンスを実現します。この記事では、WeChat アプレットにドロップダウン メニュー効果を実装し、実用的な機能を提供する方法を詳しく紹介します。

WeChat ミニ プログラムに画像フィルター効果を実装する WeChat ミニ プログラムに画像フィルター効果を実装する Nov 21, 2023 pm 06:22 PM

WeChat ミニ プログラムでの画像フィルター効果の実装 ソーシャル メディア アプリケーションの人気に伴い、人々は写真にフィルター効果を適用して、写真の芸術的効果や魅力を高めることがますます好まれています。画像フィルター効果は WeChat ミニ プログラムにも実装でき、より興味深く創造的な写真編集機能をユーザーに提供します。この記事では、WeChat ミニ プログラムに画像フィルター効果を実装する方法を紹介し、具体的なコード例を示します。まず、WeChat アプレットのキャンバス コンポーネントを使用して画像を読み込み、編集する必要があります。 Canvasコンポーネントはページ上で使用できます

WeChat アプレットを使用してカルーセル切り替え効果を実現する WeChat アプレットを使用してカルーセル切り替え効果を実現する Nov 21, 2023 pm 05:59 PM

WeChat アプレットを使用してカルーセル スイッチング効果を実現する WeChat アプレットは、シンプルで効率的な開発と使用特性を備えた軽量のアプリケーションです。 WeChat ミニ プログラムでは、カルーセル スイッチング効果を実現することが一般的な要件です。この記事では、WeChat アプレットを使用してカルーセル切り替え効果を実現する方法と、具体的なコード例を紹介します。まず、カルーセル コンポーネントを WeChat アプレットのページ ファイルに追加します。たとえば、&lt;swiper&gt; タグを使用すると、カルーセルの切り替え効果を実現できます。このコンポーネントでは、 b を渡すことができます。

WeChat アプレットに画像回転効果を実装する WeChat アプレットに画像回転効果を実装する Nov 21, 2023 am 08:26 AM

WeChat ミニ プログラムで画像回転効果を実装するには、特定のコード サンプルが必要です。WeChat ミニ プログラムは、ユーザーに豊富な機能と優れたユーザー エクスペリエンスを提供する軽量のアプリケーションです。ミニ プログラムでは、開発者はさまざまなコンポーネントと API を使用して、さまざまな効果を実現できます。その中で、画像回転効果は、ミニプログラムに興味と視覚効果を加えることができる一般的なアニメーション効果です。 WeChat ミニ プログラムで画像の回転効果を実現するには、ミニ プログラムが提供するアニメーション API を使用する必要があります。以下は、その方法を示す具体的なコード例です。

WeChatミニプログラムにスライディング削除機能を実装する WeChatミニプログラムにスライディング削除機能を実装する Nov 21, 2023 pm 06:22 PM

WeChat ミニ プログラムにスライディング削除機能を実装するには、特定のコード サンプルが必要です。WeChat ミニ プログラムの人気に伴い、開発者は開発プロセス中にいくつかの一般的な機能を実装する際に問題に遭遇することがよくあります。中でも、スライド削除機能は、よく使われる一般的な機能要件です。この記事では、WeChat アプレットにスライディング削除機能を実装する方法と具体的なコード例を詳しく紹介します。 1. 要件分析 WeChat ミニ プログラムでは、スライド削除機能の実装には次の点が含まれます。 リスト表示: スライドして削除できるリストを表示するには、各リスト項目に次の要素が含まれている必要があります。

Xianyu WeChat アプレットの名前は何ですか? Xianyu WeChat アプレットの名前は何ですか? Feb 27, 2024 pm 01:11 PM

Xianyu の公式 WeChat ミニ プログラムが静かに開始され、アイドルアイテムを簡単に公開および交換できる便利なプラットフォームをユーザーに提供します。ミニ プログラムでは、プライベート メッセージを介して購入者または販売者とコミュニケーションしたり、個人情報や注文を表示したり、欲しい商品を検索したりできます。では、WeChat ミニ プログラムでは Xianyu とはいったい何と呼ばれているのでしょうか? このチュートリアル ガイドで詳しくご紹介しますので、知りたいユーザーは、この記事に従って読み続けてください。 Xianyu WeChat アプレットの名前は何ですか? 回答: Xianyu、アイドル取引、中古品販売、評価、リサイクル。 1. ミニ プログラムでは、アイドル メッセージの投稿、プライベート メッセージを介した購入者/販売者とのコミュニケーション、個人情報と注文の表示、指定された商品の検索などができます。 2. ミニ プログラム ページには、ホームページ、近くに、アイドル投稿、メッセージ投稿、マイ投稿の5つの機能、3.

See all articles