答え:
大規模なエンタープライズ レベルのプロジェクトでは、大量のデータがレンダリングされることがよくあります。この種の長いリストは非常に一般的なシナリオです。リストの内容が増加すると、ページ スライドのフリーズや画面が白くなるなどの問題が発生します。、データのレンダリングが遅い。
この状況は主に、小さなプログラム、モバイル端末、またはバックグラウンド管理ページで発生します。
通常、ページングを使用してコンテンツを徐々に取得しますが、コンテンツが増えていくと、
たとえば、モバイル端末でプルダウンを更新すると上にスクロールし続け、下部にコンテンツが読み込まれるため、リストに多くの要素が追加され、要素が増えるとブラウザが並べ替えられて再描画されます。 、メモリ占有であれGPU レンダリングであれ、パフォーマンスが大幅に低下し、ページ スライドのフリーズやデータ レンダリングの遅延が発生します。
状況に応じて異なる処理を行う必要があります。
- 大量のデータを避ける: 取得するにはページネーションが必要です
- 大量のデータのレンダリングを避ける: vue-virtual-scrollerプラグインおよびその他の仮想リストソリューションは、ビューポート範囲内のデータのみをレンダリングします。
- 更新を避ける: v-onceを使用して1 回だけレンダリングします
- 最適化された更新: v-memoを通じてサブツリーをキャッシュし、条件付きで更新し、再利用を改善し、不必要な更新を回避します。
- データをオンデマンドでロードする:ツリーコンポーネントのサブツリーの遅延ロードなどの遅延ロードを使用します。
つまり、やはり特定のニーズに依存します。まず、デザインからの大規模なデータの取得とレンダリングを避けます。これを本当に行う必要がある場合は、仮想リストを使用してレンダリングの数を最適化し、最後に更新を最適化します。 -memoを使用すると、ビッグ データ更新のパフォーマンスをさらに最適化できます。その他の方法としては、対話型の最適化、無限スクロール、遅延読み込みソリューションなどを使用できます。
ページラグの原因
根本原因: 多数の DOM 要素のリフローと再描画
変更は現在の DOM 要素に対する変更ですが、更新はすべての DOM 要素を更新します。
最適化のアイデア:
1. 遅延レンダリング
- 一般的なロングリスト最適化ソリューションである遅延読み込みは、モバイル端末では一般的です
- 原則: 一度に一部のみがレンダリングされ、レンダリングされたデータがスクロールされようとすると、次の部分がレンダリングされます。
- 利点: 毎回データの一部を高速にレンダリングします。
- 短所: データ量が多い場合、ページ内に多数の DOM ノードが存在するため、メモリが過剰に消費され、ブラウザのレンダリング パフォーマンスが低下し、ページがフリーズします。
- 利用シナリオ:データ量が大きくない場合(各データの複雑さに応じて1000個など)
2. ページのレンダリング
通常、バックエンドはデータを提供します。ページ数と各ページに表示されるデータの量をバックエンドに渡すだけで、バックエンドは表示するデータを提供します。
3. 視覚領域のレンダリング
原則: ページの表示領域のリスト項目のみがレンダリングされ、非表示領域のデータは「まったく表示されません」(最初のいくつかの項目と次のいくつかの項目はプリロードされます)、リスト項目はリストをスクロールすると動的に更新されますが、白画面になるのを防ぐため、実際にはさらにいくつかのデータが読み込まれます。
ビューポートレンダリングを自分で実装する
親コンポーネント内で
- 計算キャッシュを使用して 10,000 個のデータをシミュレートします。
- item (データ)、size (各データの高さ)、showNumber (毎回レンダリングされるデータの数) をプロパティ バインディングを通じてサブコンポーネントに渡します。
サブコンポーネント内
- 親コンポーネントから props を通じて渡されたプロパティを受け取ります。
- 一番外側のボックスは、垂直スクロールを実現するために、overflow-y:scroll; に設定されています。
- 最も外側のボックスの固定高さ (最も外側のボックスの高さ = サイズ * showNumber);
- 外側のボックスのスクロール イベントをリッスンし、ロールアップされるデータの高さを計算します。
- 表示領域内のデータの開始インデックスを計算します。start = ロールされた高さ/単一のデータの高さです。
- 表示可能領域内のデータの終了インデックスを計算します。終了 = 開始インデックス + 表示可能領域に表示できる項目の数です。
- 表示領域の開始インデックス start と終了インデックス end のデータを取得し、表示領域に表示します。
親コンポーネント
10,000個のデータをシミュレート
10,000 個の要素を持つ空の配列を作成し、fill() 関数を使用して初期化します。すべての値は空です。その後、map を使用して ID とコンテンツを配列に返します。これにより、配列の割り当てが実現されます。
computed: {
item () {
return Array(10000).fill('').map((item, index) => ({
id: index,
content: `列表内容`+ index
}))
}
}
サブアセンブリ
UI構造
list は表示される領域です
バーはボックスを開き、垂直方向にスクロールできるようにします。
- 視覚領域コンテナ: すべての要素を保持する下部のボックスとして表示されます。
- スクロール可能領域: 中間層とみなすことができ、データが 10000 個あり、各リスト項目の高さが 50 であるとすると、スクロール可能領域の高さは 10000 * 50 になります。このレイヤーの要素は非表示であり、目的は実際のリストとまったく同じようにスクロール バーを作成することです。
- ビジュアルエリアリスト: 現在処理されているデータを表示する最上位レイヤーとみなすことができ、高さはビジュアルエリアコンテナと同じです。可視領域リストが常に可視領域に表示されるように、可視領域リストの位置が動的に変更されます。
上記の概念を理解した後、スクロール バーがスクロールするときに何をする必要があるかを見てみましょう。
- スクロールの距離と
item
高さに応じて、表示する必要がある現在のリストを計算しますstartIndex
- 表示可能領域の高さに応じ
startIndex
て、現在表示されるリストの高さを計算しますendIndex
- 対応するリストデータに応じてインターセプトし
startIndex
、endIndex
表示可能領域リストに割り当て、ページ上にレンダリングします。 - スクロール距離と
item
高さに応じて、表示領域リストのオフセット距離を計算しstartOffset
、リストに設定します
親コンポーネントから渡されたデータを受け取り、最初の添え字と終了添え字を定義します。
コンテナの高さとロールされたデータの高さを計算します
コンテナのスクロール イベントを監視します。最初の添え字は、ロールされたデータ ストリップの数を切り捨てたものです。
終了添え字は開始添え字 + 表示するデータ項目の数です。
仮想リスト vue-virtual-scrollerを使用して実装
長いリストの場合、ほとんどの操作は次のとおりです。
1. 遅延読み込み、ページネーション、
2. Object.freeze は配列をフリーズして応答をキャンセルします。これは、ほとんどの場合応答が表示されるためです。
3. 高解像度の画像をサムネイルに置き換えます。多くの場合、長いリスト内の画像のサイズは比較的小さいため、代わりに小さな画像を使用できます。
ページネーションの場合、上記の方法で長いリストの問題のほとんどを解決できます。
ページネーションが不可能な場合は、表示領域にレンダリングします
アイテムの高さは固定されています (RecycleScroller)
プラグインをダウンロード
yarn add vue-virtual-scroller --save
main.jsに登録する
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
import VueVirtualScroller from "vue-virtual-scroller";
Vue.use(VueVirtualScroller);
以下が必要です
小道具 |
説明する |
デフォルト |
アイテム |
表示領域に描画されるデータのリスト | -- |
アイテムサイズ |
各データの高さ | ヌル |
スタイル |
可視領域の外箱の高さ | -- |
v スロット = { アイテム } |
スロット、各データ項目を取得 | -- |
必要に応じて変更します
小道具 |
説明する |
デフォルト |
プリレンダリング |
毎回レンダリングするデータの数をサーバー (SSR) に指示します。 | 0 |
バッファ |
空白スクロールを避けるための、表示領域外のマルチレンダリングのデータ高さ | 200 |
キーフィールド |
アイテムを識別し、レンダリングされたビューを最適に管理するためのフィールド | ID |
固定高さスコープのスロット パラメータ
スロット |
説明する |
デフォルト |
アイテム |
各データ | -- |
索引 |
各データの添え字 | -- |
アクティブ |
ビューがアクティブかどうか。アクティブなビューは表示可能なビューとみなされ、RecycleScroller によって配置されます。非アクティブなビューは表示されているとは見なされず、ユーザーに対して非表示になります。ビューが非アクティブな場合は、レンダリング関連の計算をスキップする必要があります。 | -- |
アイテムの高さは固定されており (RecycleScroller)、引き上げてロードすることができます
イベント |
説明する |
デフォルト |
サイズ変更 |
サイズが再計算されるときにトリガーされます | -- |
見える |
スクロールバーがページ内に表示されていると判断したときに発生します。 | -- |
隠れた |
スクロールバーがページ内で非表示になったときにトリガーされます | -- |
update (startIndex、endIndex) |
: EmitUpdate="true"の場合のみ、ビューが更新されるたびに発行されます。 | 間違い |
実装アイデア:
- :emitUpdate="true" および @update イベントを追加します。
- update 関数は、start と end の 2 つの仮パラメータを渡し、end が配列リストの長さと等しいと判断して、インターフェイスに新しいデータの取得を要求し、配列に追加します。
アイテムの高さは固定されていません (DynamicScroller)。引き上げてロードすることができます
Props(参数) |
解释 |
默认值 |
item(必填) |
每项数据 | -- |
active(必填) |
保持视图,数据处于 active 状态,将防止不必要的大小重新计算。 | -- |
sizeDependencies |
影响高度的值,如果发生变化,则重新计算 | -- |
watchData |
深入监视更改以重新计算大小(不推荐,可能会影响性能) | false |
tag |
组件要呈现的元素 | div |
emitResize |
每次重新计算大小时发出事件(可能会影响性能) | false |
minItemSize |
列表项初次渲染使用的最小高度 |
Events(事件) |
解释 |
默认值 |
resize |
重新计算大小时触发,仅当 :emitUpdate=“true”时 | false |