WeChatミニプログラムでの仮想リストの実装例

WeChatミニプログラムでの仮想リストの実装例

序文

ほとんどのミニプログラムには、このような要件があります。ページに長いリストがあり、一番下までスクロールするときにバックグラウンド データを要求する必要があります。データは継続的にレンダリングされます。データ リストが長い場合、明らかな遅延が発生し、ページが白い画面で点滅します。

分析する

  • バックグラウンド データを要求するには、一定の setData と継続的なデータ マージが必要であり、後の段階で過剰なデータ レンダリングが発生します。
  • レンダリングするDOM構造は多数あり、レンダリングするたびにDOMの比較が実行されます。関連する差分アルゴリズムは時間がかかり、
  • DOM が多数あり、大量のメモリを消費するため、ページがフリーズして白い画面が表示されます。

初期レンダリング方法

/*
 * @説明: 
 * @バージョン: 
 * @著者: shijuwang
 * @日付: 2021-07-14 16:40:22
 * @LastEditors: shijuwang
 * @最終編集時間: 2021-07-14 17:10:13
 */
//ページオブジェクト
ページ({
  データ: {
    リスト: [], // すべてのデータ},
  //オプション(オブジェクト)
  onLoad: 関数 (オプション) {
    this.index = 0
    定数arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },




    ]
    this.setData({ リスト: arr })
  },

  onReachBottom: 関数 () {
    定数arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },

    ]
    {リスト} = this.dataとします
    this.setData({
      リスト: [...リスト、...arr]
    })
  },

});


// wxml 
<ビュークラス="コンテナ">
    <view class="item-list" wx:for="{{list}}">
      <テキストクラス="">
          {{item.idx}}
      </テキスト>
    </ビュー>
</ビュー>

下部をタッチするたびに、データが再度要求され、配列がマージされ、setData がリセットされます。リストが責任を負う場合、白い画面が表示されます。setData のデータは毎回大きくなり、次の図に示すように、通信時間が増加し、DOM 要素が多すぎるようにレンダリングされます。

初期最適化

1. 1次元配列を2次元配列に変更する

  • 通常、setData はすべてのデータを再レンダリングします。
  • ページ分割されたリクエストはサーバーにかかる負荷が比較的少なく、ページ分割された増分レンダリングもsetDataにかかる負荷が比較的少ない。
  • setDataは2次元配列のインデックスに対応するデータの添え字を検索し、setDataを使用して部分的な更新を実行します。
  • setDataの場合、現在の画面のデータのみが割り当てられます
// wxml
<ビュークラス="コンテナ">
  <block wx:for="{{list}}" wx:for-index="pageNuma">
    <view class="item-list" wx:for="{{item}}">
      <text class="">{{item.idx}}</text>
    </ビュー>
  </ブロック>
</ビュー>
// wx.js
ページ({
  データ: {
    リスト: [], // すべてのデータ},
  //オプション(オブジェクト)
  onLoad: 関数 (オプション) {
    this.index = 0;
    this.currentIndex = 0; // 現在のページ番号 pageNuma
    定数arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },




    ]
    this.setData({ [`list[${this.currentIndex}]`]: arr })
  },

  onReachBottom: 関数 () {
    this.currentIndex++; // 一番下まで +1
    定数arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },

    ]
    this.setData({
      [`list[${this.currentIndex}]`]: arr
    })
  },
}); 

このように、レンダリング全体が画面に基づいてページ単位でレンダリングされることがわかります。複数の pageNums が要求された場合、複数の画面がレンダリングされ、各リストが各画面にレンダリングされます。

さらなる最適化

2 可視領域の 1 つの画面のみをレンダリングするか、可視領域の近くに複数の画面をレンダリングし、ビューを使用して特定のレンダリングを行わずに他の領域を占有することで、DOM レンダリングを減らし、ノードが多すぎることによる問題を軽減できます。

これを行うには、次の手順を分割する必要があります。

  • 現在の画面の高さを取得し、レンダリング時に各画面の高さを取得し、スクロール距離を計算します
  • 取得したレンダリングデータをすべて保存し、各画面の高さを保存し、onPageScrollを通じて可視領域がどの画面にあるかを計算します。
  • 表示領域を除いて、他の領域のデータは空であり、ビューはその場所を占めるために使用される
    is.currentIndex = 0; // 現在のページ番号 pageNum
    this.pageHeight = []; //各画面の高さを保存 this.allList = []; //すべてのデータを取得 this.systemHeight = 0; //画面の高さ this.visualIndex = []; //表示領域pageNumを保存

ページに入るときに、関連データをマウントします。

 // wxml
 <view wx:for="{{list}}" wx:for-index="pageNum" id="item{{pageNum}}">
    <view class="item-list" wx:for="{{item}}">
      <text class="">{{item.idx}}</text>
    </ビュー>
  </ビュー>
 onLoad: 関数 (オプション) {
    this.index = 0;
    this.currentIndex = 0; // 現在のページ番号 pageNum
    this.pageHeight = []; // 各画面の高さが保存されます this.allList = []; // すべてのデータが取得されます this.systemHeight = 0; // 画面の高さ const arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      }
    ]
    this.setData({ [`list[${this.currentIndex}]`]: arr }, () => {

      ページの高さを設定します。
    });
    システム情報を取得します。
  },

各画面のビューのID名を動的に設定し、レンダリング中に各画面の高さの取得と保存を容易にします。ループpageHeightを使用して、後続の非表示領域の配置の高さを設定します。各ページの高さと現在のスクロール距離を比較に追加して、現在レンダリングされているpageNumを取得し、現在選択されているレンダリングされたpageNum-+1、前の画面、次の画面を配列に入れます。現在の3ページのデータが表示され、他の画面のデータはビューの配置に使用され、高さは保存されている高さです。

   // スクロール距離の計算 onPageScroll: throttle(function (e) {
    pageScrollTop = e[0].scrollTopとします。  
    that = this とする;
    // 現在のページ画面を計算するためにスクロールします。let scrollTop = 0;
    currentIndex を this.currentIndex とします。

    (var i = 0; i < this.pageHeight.length; i++) {
      scrollTop = scrollTop + this.pageHeight[i];

      (スクロールトップ>ページスクロールトップ+this.systemHeight - 50){
        this.currentIndex = i;
        this.visualIndex = [i - 1, i, i + 1];
        that.setData({
          ビジュアルインデックス: this.visualIndex
        })
        壊す;
      }
    }
  },200)

スクロールとスロットリングの最適化のリアルタイム監視

const スロットル = (fn, 間隔) => {
  var enterTime = 0; //トリガー時間 var gapTime = interval || 300; //間隔時間。間隔が経過しない場合、デフォルトは300msです。
  関数を返す(){
    var that = this;
    var backTime = new Date(); //関数が最初に返される時刻がトリガーされます if (backTime - enterTime > gapTime) {
      fn.call(that, 引数);
      enterTime = backTime; // 最初のトリガーの時間を割り当てて、2 番目のトリガーの時間を保存します}
  };
}

表示領域の配列を取得し、現在の pageNum が visualIndex 内にあるかどうかを判断します。ある場合はレンダリングします。ない場合は、ビューがスペースを占有し、高さが取得されて保存されます。

<wxs モジュール = 'フィルター'>
 var includeList = function(リスト、現在のインデックス){
   if(リスト){
    list.indexOf(currentIndex) > -1 を返す
   }
 }
 module.exports.includesList = includeList;
</wxs>
<ビュークラス="コンテナ">
 <view wx:for="{{list}}" wx:for-index="pageNum" id="item{{pageNum}}" wx:key="pageNum">
   <block wx:if="{{filter.includesList(visualIndex,pageNum)}}">
     <view class="item-list" wx:for="{{item}}">
       <text class="">{{item.idx}}</text>
     </ビュー>
   </ブロック>
   <ブロック wx:else>
     <view class="item-visible" style="height:{{pageHeight[pageNum]}}px"></view>
   </ブロック>
 </ビュー>
</ビュー>

表示領域内の配列の pageNum が現在の配列にない場合は、高さのプレースホルダーが設定されます。レンダリング効果は次のようになります。

この方法はうまくいきます!

方法2

WeChatミニプログラムAPIの使用
インターセクションオブザーバー

  //ビューモニター observePage: function (pageNum) {
    const that = this;
    const observerView = wx.createIntersectionObserver(this).relativeToViewport({ top: 0, bottom: 0});
    observerView.observe(`#item${pageNum}`, (res) => {
      コンソールにログ出力します。
      (res.intersectionRatio > 0)の場合{
        that.setData({
          ビジュアルインデックス: [ページ番号 - 1、ページ番号、ページ番号 + 1]
        })

      }
    })
  }

oberserve を使用して、現在のページが可視領域内にあるかどうかを監視します。そうである場合は、現在の pageNum -+1 を格納し、pageNum を可視領域配列 visualIndex に格納します。wxs を使用して、現在の pageNum が可視領域配列内に存在するかどうかを判断します。存在する場合は、それをレンダリングします。存在しない場合は、view を使用してその場所を占有します。

解決策 1: スクロール計算 >>>コード スニペット: (スクロール仮想リスト)

ソリューション 2 IntersectionObserver API >>> コード スニペット: intersectionObserver

WeChatミニプログラムバーチャルリストの実装例に関するこの記事はこれで終わりです。より関連性の高いミニプログラムバーチャルリストのコンテンツについては、123WORDPRESS.COMの以前の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後も123WORDPRESS.COMを応援してください。

以下もご興味があるかもしれません:
  • WeChatアプレット仮想リストの応用例

<<:  Linux システムで Tomcat のポート 80 を使用する方法

>>:  MySQL のインデックスにおける NULL の影響についての詳細な説明

推薦する

Nginx サービス クイック スタート チュートリアル

目次1. Nginx の紹介1. Nginx とは何ですか? 2. Nginx を使用する理由3. ...

Linux の crontab タスク スケジューリングの簡単な分析

1. スケジュールタスクを作成する命令crontab -eは現在のユーザーの編集インターフェースに入...

MySQL 実行ステータスの表示と分析

MySQL のパフォーマンスに問題があると思われる場合は、通常、まずshow processlist...

Ajax jQueryはページ上のdivの更新効果を実現します

元のコードは次のとおりです。 <div class='コントロールグループ'&...

CSS3 は下部に固定されたフッターを実装します (ページの高さに関係なく常に下部にあります)

序文フッター領域を下部に固定します。ページの高さや幅に関係なく、モバイル メニューと同様に、フッター...

Linux リモートログイン実装チュートリアル分析

Linux は一般的にサーバーとして使用され、サーバーは一般的にコンピュータルーム内に置かれます。L...

Vue ディレクティブ v-html と v-text

目次1. v-text テキストレンダリング命令2. v-html 1. v-text テキストレン...

WAMPにインストールするとMySQLが起動できるが、再起動後に起動できなくなる問題の解決方法

初めてwampをインストールした後、すべてのサービスが正常に使用できますが、再起動するとwampのア...

MySQL スロークエリログの役割と公開

序文MySQL スロー クエリ ログは、MySQL が提供するログ レコードの一種です。これは、応答...

キャンバスをベースにした超クールな水光効果を実現

この記事の例では、キャンバスをベースにした超クールな水の光の効果を実装するための具体的なコードを参考...

Linuxファイルを表示するコマンドの詳細な説明

Linuxファイルを表示する方法ファイルの内容を表示するコマンド: catは最初の行からコンテンツを...

MySQLフィールドのデフォルト値を設定する方法

目次序文: 1. デフォルト値に関する操作2. 使用上の提案要約:序文: MySQL では、テーブル...

Docker が MySQL を作成する説明

1. MySQLイメージをダウンロードするコマンド: docker pull mysql 2. コン...

Docker で Portainer ビジュアル インターフェースを構築するための詳細な手順

前回述べた問題を解決するために、オンラインで検索したところ、非常に優れたビジュアル インターフェース...

MySQL 8.0.16 Win10 zip バージョンのインストールと設定のグラフィック チュートリアル

この記事では、MySQL 8.0.16 Win10 zip版のインストールと設定のグラフィックチュー...