Vue における v-for のキーの一意性の詳細な説明

Vue における v-for のキーの一意性の詳細な説明

1. DOM の違い

キー属性の重要性を本当に理解するには、まず DOM Diff から始める必要があります。DOM Diff の原理を深く理解する必要はなく、DOM Diff の動作プロセスを知るだけで十分です。

Vue と React はどちらも仮想 DOM を使用して、不要なブラウザ レンダリングを削減します。 Vue と React はどちらもビューをレンダリングするために v = render(m) メソッドを使用するため、モデル データが変更されると、DOM 要素が再レンダリングされてビューが更新されます。しかし、コンポーネント内の div のデータだけを変更する場合もあります。ネイティブ レンダリングを使用してビューを更新する場合は、コンポーネント全体を更新する必要があります。時間の無駄じゃないですか?

日常生活でこのような状況に遭遇した場合、すべてのピースを更新することはありません。パズルが完成した後、小さなピースの 1 つを交換する必要があるようなものです。ピースを見つけて直接交換するだけです。最初からやり直すことはありません。 Vue と React の開発者も同様に考えており、最適化するためにあらゆる可能な方法を試します。

人間の目には変更前と変更後の違いが一目でわかるので、その違いだけを更新すればいいのです。しかし、コンピューターはそれを一目で見ることはできません。違いを見つけて更新するまで、最初から素早く比較する必要があります。変更前と変更後を比較して相違点を見つけるプロセスを DOM Diff と呼びます。DOM Diff の DOM は仮想 DOM、つまり JavaScript オブジェクトです。1 つずつ比較して相違点を見つけた後、実際の DOM を部分的に更新します。

比較プロセス中に、仮想 DOM も仮想 DOM ツリーを形成します。DOM Diff の動作プロセスは、2 つの仮想 DOM ツリー上のオブジェクト ノードを比較することであり、具体的には各レイヤーの対応する位置を比較します。コンピュータは各レイヤーの対応する位置にある 2 つの仮想 DOM 要素のみを比較するため、2 つのツリー内の変更されたツリーのレイヤーに 1 つのノードのみが挿入された場合、ツリーの構造は変更されず、次の図に示すように、このレイヤーを比較するときに DOM Diff によって不整合な比較が発生します。

このレベルの仮想 DOM ノードは、DOM ノード自体を除いて Vue と React で完全に同じであるため、DOM Diff は比較時に対応する位置を 1 つずつしか比較できません。

1 対 1 の比較後、ノード タイプが同じであれば、ノードは再利用され、ノード内の異なるコンテンツのみが部分的に更新されます。上図に示すように、これが ul の下の li の仮想 DOM ノードである場合、1 つずつ比較してノード タイプが同じであることがわかった後、前のノードが再利用され、ノード内のコンテンツが変更されます。つまり、C は F に更新され、D は C に更新され、E は D に更新され、最後に E が挿入されます。

上記はノードを挿入する場合であり、その結果は効率の低下です。しかし、ノードを削除する場合、結果は効率だけではありません。

li 要素を削除するボタンをクリックすると、新しい仮想 DOM ツリーと古い仮想 DOM ツリーを比較するときに、ツリー内の各レイヤーの対応する位置に従って 1 つずつ比較されます。たとえば、削除後、[1,2,3] は [1,3] になります。最初の li と 2 番目の li を比較します。要素タイプが変更されていないことが判明した場合は、最初の li を再利用し、その中の li を再帰的に比較します。変更がないことが判明した場合は、再利用を続けます。 2 番目の li 要素を比較すると、それらも li 要素であることがわかるため、前の li が再利用され、2 のみが 3 に変更されます。

このとき、再利用された li にサブ要素があり、サブ要素が依存するデータが変更されていない場合、以前のサブコンポーネントが引き続き再利用され、次の図に示すように不整合が発生します。

2. 同じレイヤーの同じタイプの要素にキー属性を追加する

上記の DOM Diff アルゴリズムでは、2 つのツリーの同じレイヤーの対応する位置のみが比較されます。異なるレイヤー間の要素を比較する必要はありません。さらに、DOM Diff プロセスで、変更された仮想 DOM が以前の仮想 DOM と異なるタイプであることが判明した場合、以前の仮想 DOM はアンインストールされ、変更された要素ノードが再度追加されます。したがって、2 つのツリー内の同じレイヤーのノード タイプが同じである場合に上記の問題が発生し、レイヤーを追加または削除すると効率が低下したり、バグが発生したりします。

これは、v-for ループで同じタイプのラベル要素を生成すると発生します。ラベル ノードに対して何もしないと、バグが発生するリスクがあります。では、どうすればよいでしょうか。

答えは、同じレイヤーにある同じノード タイプのノードに一意のキー値を追加することです。このようにして、DOM Diff がペアワイズ比較を実行すると、対応する位置に従って比較するのではなく、同じキーを持つ 2 つの仮想 DOM が比較されます。

これにより、不整合な比較が回避され、比較の効率が大幅に向上し、バグのリスクが解決されます。

3. キーはインデックスの添え字値にはならない

配列やオブジェクトのインデックス値は一意であるため、キー属性の値としてインデックスを使用することがよくあります。これは問題なく、パフォーマンスの最適化などをもたらすと言う人もいますが、インデックス値を使用すると大きなバグのリスクが発生します。

これらのバグは、v-for ループ内のオブジェクトまたは項目が追加、削除されたり、順序が変更されたりしたときに発生します。

では、なぜインデックス添え字を使用できないのでしょうか?

実は、インデックスの添え字が使われたり使われなかったりするからです。追加や削除をすると、特定の要素のインデックスが変わるからです。例えば、[1,2,3]が[1,3]になった後、データ3に対応する元の添え字は2で、削除後はデータ3の添え字は1になります。DOM Diffを実行すると、等しいキー値に基づいてペアワイズ比較が実行されます。データ3に対応するノードは、まだ互いに対応していません。そのため、インデックスをキーとして使用すると、キーを設定しないのと同じ効果があります。

これが、インデックスをキーとして使用すべきではない理由です。

したがって、キー属性値は一意であり、変更されない必要があります。

上記は、vue における v-for のキー一意性についての詳細な説明です。vue における v-for のキー一意性についての詳細は、123WORDPRESS.COM の他の関連記事にも注目してください。

以下もご興味があるかもしれません:
  • Vue の v-for ループの主要属性に関する考慮事項のまとめ
  • vue v-for のループをトラバースするときにキー値エラーが発生する問題を解決する
  • Vueのキー属性を理解する方法の詳細な説明
  • Vue におけるキー値の重複問題

<<:  CentOS 7 でゲートウェイを変更して IP を設定する方法の例

>>:  MySQLのテーブル構造を変更する際に知っておきたいメタデータロックの詳しい解説

推薦する

JavaScript を使用してセカンダリ メニューを作成する

この記事では、セカンダリメニュー効果を実現するためのJavaScriptの具体的なコードを参考までに...

MySQL 変数の原理と応用例

MySQL ドキュメントでは、MySQL 変数はシステム変数とユーザー変数の 2 つのカテゴリに分類...

HTML ページでギリシャ文字を使用する方法

ギリシャ文字は、特に数学や物理学などの科学技術分野で非常によく使用される記号列であり、特定の意味を持...

Linux システムをバックアップする docker コマンドの詳細な説明

tar バックアップ システム sudo tar cvpzf backup.tgz --exclud...

Node.js+expressメッセージボード機能実装例

目次メッセージボード必要なライブラリオープンソースプロジェクトプロジェクト構造メッセージボードnod...

Linuxのbasenameコマンドの使い方

01. コマンドの概要basename - ファイル名からディレクトリとサフィックスを削除しますba...

MySQL 10進数符号なし更新負数を0に変換

今日、インターフェースの同時実行の問題を検証したところ、これまでredisで解決していた同時実行のプ...

nginx が動的と静的の分離を実装する方法の例

目次server1にnginxをデプロイするサーバーにlnmpを展開するノード3にhttpdをデプロ...

PHP クラスにおける static と self の違いの簡単な分析

メソッドが定義されているクラスに応じて、現在のクラスへの静的参照を取得するには、self:: または...

JS の compose 関数と pipe 関数の使い方の詳細な説明

目次作成機能配列プロトタイプの削減Array.prototype.reduceRightパイプ関数作...

Linux/Mac MySQL パスワードを忘れた場合の対処方法

Linux/Mac の MySQL パスワードを忘れた場合はどうすればいいですか?心配しないでくださ...

ネイティブ JS カプセル化 vue タブ切り替え効果

この記事の例では、ネイティブJSカプセル化vueタブ切り替えの具体的なコードを参考までに共有していま...

Vue+echarts で積み上げ棒グラフを実現

この記事では、積み上げ棒グラフを実装するためのVue+echartsの具体的なコードを参考までに紹介...

CentOS 7 での Docker プロキシの設定 (Linux での Systemd サービスの環境変数設定)

Docker デーモンは、 HTTP_PROXY 、 HTTPS_PROXY 、およびNO_PRO...

ファイルアップロードスタイルの詳細を実装するjs

目次1. 概要2. オブジェクト作成のパラメータ3. 監視例4. 使用方法5. ソースコード1. 概...