序文モバイル Web ページの日常的な開発では、長いリストをレンダリングする必要があるシナリオが時々あります。たとえば、旅行 Web サイトでは、全国の都市のリストを完全に表示し、次にアドレス帳のすべての名前を A、B、C のアルファベット順に表示する必要があります... 通常、長いリストの数が数百以内であれば予期しない影響はなく、ブラウザ自体も十分にサポートできます。ただし、数が数千に達すると、ページのレンダリング プロセスが明らかに停止します。数が数万、さらには数十万を超えると、Web ページが直接クラッシュする可能性があります。 長いリストによって引き起こされるレンダリング圧力を解決するために、業界では対応する対応技術、つまり長いリストの仮想スクロールが開発されました。 仮想スクロールの本質は、ページがどのようにスライドしても、HTML ドキュメントは現在の画面ビューポートに表示される少数の DOM 要素のみをレンダリングすることです。 長いリストに 10 万件のデータがあると仮定すると、ユーザーにとって画面に表示されるのは 10 数件のデータだけです。そのため、ページがスライドするときに、スクロール イベントをリッスンしてビューポート データをすばやく切り替えることで、スクロール効果を高度にシミュレートできます。 仮想スクロールでは、同様のスクロール効果をシミュレートするために少数の DOM 要素をレンダリングするだけで済むため、フロントエンド エンジニアは数万、あるいは数十万もの項目からなる長いリストを開発できるようになります。 次の画像は、携帯電話で見ることができる世界中の都市の長いリストを示しています (ソース コードは記事の最後に掲載されています)。 ローリング原理仮想スクロールの仕組みを理解するには、まず次の図をご覧ください。指を下にスライドすると、HTML ページも上にスクロールします。 リアルなスクロール効果をシミュレートするには、まず仮想スクロールが次の 2 つの要件を満たす必要があります。
上記の要件を満たすための HTML デザイン構造は次のようになります。 .wrapper は最も外側のコンテナ要素であり、位置は絶対または相対に設定され、子要素はそれに基づいて配置されます。 サブ要素 .background と .list は、仮想スクロールの鍵となります。.background は空の div ですが、長いリスト内のすべてのリスト項目の高さの合計と同じ高さに設定する必要があります。さらに、絶対位置に設定し、z-index 値を -1 に設定する必要があります。 .list は、ビューポートによって観察される Dom 要素を動的にレンダリングする役割を担い、位置は絶対位置に設定されます。 <テンプレート> <div class="wrapper"> <div class="background" :style="{height:`${total_height}px`}"></div> <div class="list"> <div class="line"> <div class="item lt">北京</div> <div class="item gt">北京</div> </div> <div class="line"> <div class="item lt">上海</div> <div class="item gt">上海</div> </div> <div class="line"> <div class="item lt">広州</div> <div class="item gt">広州</div> </div> ... //省略</div> </div> </テンプレート> <style lang="scss" スコープ> .ラッパー{ 位置: 絶対; 左: 0; 右: 0; 下部: 0; 上: 60px; overflow-y: スクロール; 。背景 { 位置: 絶対; 上: 0; 左: 0; 右: 0; Zインデックス: -1; } .リスト{ 位置: 絶対; 上: 0; 左: 0; 右: 0; } } </スタイル> 上記コードで total_height が 10000px に等しい場合、ページ実行効果図は次のようになります。 スクロール バーの問題は解決しましたが、スクロール バーが下にスライドすると、データ リストが上に移動します。リストが完全に画面から移動した後、次のスライドはすべて白い画面になります。 次のアニメーションを観察してください。右側の Dom 構造はスライドするときの変化を示しています。 スクロール バーがすばやく下にスライドすると、リストの DOM 要素がすばやくレンダリングされ、更新されます。このとき、.list 内の DOM 要素が絶えず置き換えられるだけでなく、.list 要素自体も transform: translate3d(0, ? px ,0) スタイル値を絶えず変更しています (translate3d を変更すると、top 属性値を変更するのと同様の効果が得られます)。 以上の説明から、仮想スクロールの実装ロジックは明らかです。まず、js はスクロールバーのスライドイベントを監視し、スライド距離に応じて .list 要素のどのサブ要素をレンダリングするかを計算し、.list 要素の位置を更新します。スクロールバーがスライドし続けると、サブ要素と位置も常に更新され、ビューポート上でスクロール効果がシミュレートされます。 成し遂げる開発されたデモ ページを下図に示します。リスト項目には次の 3 つの構造が含まれています。
リスト データ city_data の JSON 構造は次のようになります。type 1 は小さなリスト項目のスタイル構造レンダリングを表し、2 は通常のリスト項目を表し、3 は大きなリスト項目を表します。
city_data には長いリスト内のすべてのデータが含まれています。city_data を取得したら、まず各項目のデータ構造を走査して調整します (コードは次のとおりです)。 次のように処理すると、各リスト項目には最終的に top 値と height 値が含まれます。top 値は長いリストの先頭からの項目の長さを示し、height 値は項目の高さを示します。 マウントされた(){ function getHeight (type) { // タイプ値に応じて高さを返す switch (type) { ケース1: 50を返します。 ケース2: 100を返します。 ケース3: 150を返します。 デフォルト: 戻る ""; } } total_height を 0 にします。 const list = city_data.map((データ, インデックス) => { 定数height = getHeight(data.type); 定数ob = { 索引、 身長、 上: 合計高さ、 データ } total_height += 高さ; ob を返します。 }) this.total_height = total_height; // リストの合計の高さ this.list = list; this.min_height = 50; // 最小の高さは50です // 画面に表示できるリスト項目の最大数。containerHeight は親コンテナーの高さで、最小の高さに基づいて計算されます。this.maxNum = Math.ceil(containerHeight / this.min_height); } HTML は、type 値に応じて異なるスタイル構造をレンダリングします (コードは次のようになります)。親コンテナー .wrapper はスライド イベント onScroll をバインドし、リスト要素 .list は this.list 配列をトラバースしません。これは、this.list がすべてのリスト項目を含む元のデータであるためです。 <template> テンプレートは、ビューポートに表示する必要があるデータ runList を走査するだけで済みます。配列 runList に含まれるデータは、スクロール イベントによって継続的に更新されます。 <テンプレート> <div class="wrapper" ref="wrapper" @scroll="onScroll"> <div class="background" :style="{height:`${total_height}px`}"></div> <div class="list" ref="コンテナ"> <div v-for="runList 内のアイテム" :class="['line',getClass(item.data.type)]" :key="item"> <div class="item lt">{{item.data.name}}</div> <div class="item gt">{{item.data.value}}</div> <div v-if="item.data.type == 3" クラス="img-container"> <img src="../../assets/default.png" /> </div> </div> </div> </div> </テンプレート> スクロール イベントは onScroll メソッドをトリガーします (コードは以下のとおり)。スクロール バーは非常に頻繁にトリガーされるため、ブラウザーによる計算量を減らすために、requestAnimationFrame 関数を使用して調整します。 スクロール イベント オブジェクト e は、現在のスクロール バーのスライド距離を取得できます。距離に基づいて、runList のリスト データを計算し、.list の位置情報を変更するだけです。 スクロールの(e){ if (this.ticking) { 戻る; } this.ticking = true; リクエストアニメーションフレーム(() => { this.ticking = false; }) 定数距離 = e.target.scrollTop; this.distance = 距離; this.getRunData(距離); } スクロール距離に基づいて、画面ビューポートの下にレンダリングする必要がある最初のリスト項目要素をすばやく見つけるにはどうすればよいでしょうか? this.list は長いリストのデータ ソースであり、各リスト項目には長いリストの先頭からの距離 top と、その項目自体の高さ height が格納されます。 ページが少し上に移動すると、ビューポートの下の最初のリスト項目の一部だけが表示され、他の部分は画面外になります。この時点では、ビューポートの下の開始要素が画面から完全に外れるまで上に移動しない限り、ビューポートの下の開始要素は依然としてリスト項目であると判断されます。 次に、ビューポートにレンダリングされる最初の要素を判断する基準は、ページの scrollTop がリスト項目要素の上部と上部 + 高さの間にあることです。 上記の原則に基づいて、バイナリ検索を使用して高速クエリを実現できます (コードは次のとおりです)。 // バイナリ検索を使用して開始インデックスを計算します。scrollTop はスクロール距離です。getStartIndex (scrollTop) { 開始 = 0、終了 = this.list.length - 1 とします。 (開始 < 終了) の間 { const mid = Math.floor((開始 + 終了) / 2); const { top, height } = this.list[mid]; スクロールトップ >= 上 && スクロールトップ < 上 + 高さ) { 開始 = 途中; 壊す; } そうでない場合 (scrollTop >= top + height) { 開始 = 中間 + 1; } そうでない場合 (スクロールトップ < トップ) { 終了 = 中間 - 1; } } 開始を返します。 } バイナリ メソッドは、this.list 配列のビューポートの下にレンダリングされた最初の要素のインデックスを計算します。このインデックスは開始インデックス start_index と呼ばれます。次に、コア関数 getRunData を入力します (コードは次のとおりです)。主に次の 2 つの処理を行います。
実際の開発では、画面の高さが 1000px、最小のリスト項目が 50px であると仮定すると、画面に収容できるリスト項目の最大数 this.maxNum は 20 になります。 this.runList が 1 つの画面に収まる最大数の項目のみを保持している場合、スクロール バーが高速にスクロールすると、インターフェイスのレンダリング速度が指のスライド速度に追いつかず、下部に白い画面が点滅します。 この問題の解決策は、HTML ドキュメントにバッファリングされたデータをもう少しレンダリングすることです。たとえば、以下の getRunData 関数は、それぞれ上部の画面、中央の画面、下部の画面に対応する 3 つの画面の高さに対応できるリスト項目の数をレンダリングします。 中央の画面は現在のビューポートに対応する画面で、上下の画面にはビューポートの上下に表示されていないバッファリングされた Dom が格納されます。まず、バイナリ検索方式を使用して、画面ビューポートの下の最初のリスト項目要素のインデックス start_index を照会し、次に start_index に基づいて上下の画面の最初のリスト項目のインデックスも簡単に取得できます。 getRunData(距離 = null) { //スクロール距離 const scrollTop = distance ? distance : this.$refs.container.scrollTop; //スクロールが実行されない範囲 if (this.scroll_scale) { (スクロールトップ>this.scroll_scale[0]&&スクロールトップ<this.scroll_scale[1]){ 戻る; } } //開始インデックス let start_index = this.getStartIndex(scrollTop); start_index = start_index < 0 ? 0 : start_index; // 上部画面インデックス、this.cache_screens のデフォルトは 1 で、1 つの画面をキャッシュします。let upper_start_index = start_index - this.maxNum * this.cache_screens; 上位開始インデックス = 上位開始インデックス < 0 ? 0 : 上位開始インデックス; // オフセットを調整する this.$refs.container.style.transform = `translate3d(0,${this.list[upper_start_index].top}px,0)`; //中央の画面の要素 const mid_list = this.list.slice(start_index, start_index + this.maxNum); // 上部画面 const upper_list = this.list.slice(upper_start_index, start_index); // 下画面要素 let down_start_index = start_index + this.maxNum; down_start_index = down_start_index > this.list.length - 1 ? this.list.length : down_start_index; this.scroll_scale = [this.list[Math.floor(upper_start_index + this.maxNum / 2)].top、this.list[Math.ceil(start_index + this.maxNum / 2)].top]; 定数 down_list = this.list.slice(down_start_index, down_start_index + this.maxNum * this.cache_screens); this.runList = [...上位リスト、...中間リスト、...下位リスト]; } スクロール イベントは非常に頻繁にトリガーされます。開発者としては、ブラウザの計算量をできるだけ減らす必要があります。そのため、スクロール範囲をコンポーネントにキャッシュできます。つまり、配列 this.scroll_scale (データ構造は [5000,5675] に似ています)。スライド距離がこの範囲内であれば、ブラウザはリスト データを更新する必要はありません。 スクロール距離 scrollTop がスクロール範囲内に入ると、getRunData 関数は何も行いません。指をスライドすると、デフォルトのスクロール動作を使用して、.list 要素が指で上下に移動します。 スクロール方向が下方向であると仮定すると、scrollTop がスクロール範囲外になったとき、スライディング viewport.wrapper の上端が次のリスト項目の上端と一致した瞬間に、getRunData 関数はまず開始インデックス start_index を計算し、次に start_index を通じて上画面の最初の要素のインデックス upper_start_index を取得します。 コンポーネントが以前にマウントされたときに、各リスト項目は長いリストの先頭からの距離をキャッシュしていたため、.list 要素を割り当てる位置情報は this.list[upper_start_index].top を通じて取得できます。次に、新しいリスト データ runList が再計算されてページがレンダリングされ、新しい状態でのスクロール範囲がキャッシュされます。 これまで、上記の手順により仮想スクロールが実現されました。上記で紹介した実用的な方法は非常に簡単に使用できますが、設計者は設計案を計画する際に、まずさまざまなスタイルのリスト項目の高さを定義する必要があります。 リスト項目の高さを内部のコンテンツに応じて自然に拡張する必要があり、ページ設計時に固定できない場合は、次の参考記事を読んで実現できます。 ソースコードソースコード 参照する10万件のデータを高性能にレンダリング初心者でもわかる仮想スクロールの実装方法仮想リストの実装原理の簡単な紹介 以上で、Vue で仮想スクロールを簡単に実装するサンプルコードについての説明は終了です。Vue の仮想スクロールに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: centos7.2 オフラインインストール mysql5.7.18.tar.gz
>>: Win10 での MySQL 5.7 の詳細なインストールと設定のチュートリアル
背景:サイトはフロントエンドとバックエンドから分離されています: vue+springbootフロン...
使用フレキシブル ボックスはフロントエンドの Web ページ レイアウトで重要な役割を果たしますが、...
データベースバージョン: mysql> select version(); +--------...
目次序文1. 404 ページ1. 原因2. 解決策2.白い画面を更新する1. 原因2. 解決策3. ...
天気予報をウェブサイトに挿入すると、次のような効果が得られます。次のコードを挿入する必要があります:...
mysql5.7.18の解凍版はmysqlサービスを起動します。具体的な内容は以下のとおりです。 1...
「脳が多数の領域間の関係を処理できるように、入力は論理的なグループに分割する必要があります。」 – ...
大学院入試に備えて、C/C++ を使って基本的なデータ構造とアルゴリズムを実装する予定です。アルゴリ...
目次MySQL 5.6以前MySQL 5.6以降要約する知らせMySQL 5.6以前更新手順元のテー...
1. 公式ウェブサイトからMySQLをダウンロードします。 これが私たちが探しているものです、win...
この記事では、参考までにMySQL 8.0.19 winx64のインストールチュートリアルを紹介しま...
フロントエンド開発を行うと、PCとモバイル端末の適応に必然的に直面することになります。このような問題...
node を D ドライブにインストールしましたが、C ドライブのスペースを占有したくなかったため、...
CI/CD の概要CIワークフロー設計Gitコードバージョン管理システムはコマンドラインでのみ管理で...
セルでは、明るい境界線の色を個別に定義できます。 > 基本構文<TD ボーダーカラーライ...