Vueデータ監視の原理の詳細な説明

Vueデータ監視の原理の詳細な説明

1. はじめに

まずは簡単な絵を描きます。

Vue を書くときは、常にデータを扱います。データにターゲット データを書き込み、テンプレートの差分式の {{xxx}} 形式を通じてデータをレスポンシブにレンダリングします。データ内のデータが変更されると、ここにあるオレンジ色の線によって差分式が変更されます。そこで疑問になるのが、データの変化をどのように監視するかということです。これには、Vue 監視データの問題が関係します。

II. 監視対象

2.1 なぜオブジェクトを監視する必要があるのですか?

まず、必要な静的ページを作成します。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>データの更新</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<本文>
    <div id="ルート">
        <ul>
            <li v-for="p in persons" :key="p.id">
                {{p.name}} - {{p.age}} = {{p.sex}}
            </li>
        </ul>
    </div>
 
    <script type="text/javascript">
        Vue.config.productionTip = false
        定数vm = 新しいVue({
            el: '#root',
            データ:{
                人数:
                    {id:'001',name:'黒猫几绛',age: 20, sex: '不明'},
                    {id:'002',name:'白猫几绛',age: 23, sex: '男'},
                    {id:'003',name:'阿猫几绛',age: 18, sex: '女'}
                ]
            }
        })
    </スクリプト>
</本文>
</html>

ここで、ページにボタンを追加して、ID 002 のデータを変更できるようにする必要があるとします。

<button @click="change">クリックして Baimao Jijiang を変更します</button>
 
  方法:{
      変化(){
          this.persons[1].name = 'ホワイトホース'
          this.persons[1].age = 17
          this.persons[1].sex = '不明'
      }
  }

属性を一つずつ変更することで、ID 002 のデータを変更することができますが、これは少し面倒でしょうか? このような長い変更をオブジェクトに変更することはできますか?

 this.persons[1] = {id:'002',name:'白马',age: 17, sex: '不明'}

現時点では、ブラウザでボタンをクリックしてもページは応答しません。しかし、予想外に、vmインスタンスオブジェクトでは、persons[1]のデータが実際に変更されていました。

この問題を解決するには、まず別の例を見て、次にブラウザで出力して具体的な実行プロセスを確認し、監視方法を理解してからこの問題に戻ります。

2.2 データエージェント

データ監視を理解するには、前提条件となる概念であるデータ エージェントも理解する必要があります。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>監視オブジェクト</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<本文>
    <div id="ルート"></div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        定数vm = 新しいVue({
            el: '#root',
            データ:{
               名前: 「黒猫ジジャン」
               年齢: 20
            }
        })
    </スクリプト>
</本文>
</html>

以前は、データ内のデータにアクセスしたい場合、vm.name や vm.age などのインスタンス オブジェクトを通じて特定の値を直接取得できました。実際、これはデータプロキシを行った後の簡略化された書き方です。

Vue はデータを処理し、そのデータを _data に格納します。インスタンス オブジェクトは、._data を通じて処理されたデータを取得できます。唯一の違いは、属性の値が直接指定されなくなり、get メソッドを介して取得されることです。これは、JAVA などのオブジェクト指向言語でクラス内のプライベート変数を取得するために使用される getter メソッドに多少似ています。ここには set メソッドもあります。データ内のデータ値が変更されると、set メソッドが呼び出され、テンプレートが再解析され、新しい仮想 DOM が生成されて新しい DOM と古い DOM が比較され、最終的にページが更新されます。

実際には、これにはソース コードの問題が関係します。この記事ではソース コードについては考慮しません。そうでないと、複雑になりすぎます。簡単に言うと、Vue 内には実際にそのようなメソッドがあります。

vm._data = データ = 新しいオブザーバー(データ)

Observer 関数では、まず Object.keys を使用してデータのすべてのプロパティを取得して配列を形成し、次に配列を走査し、Object.defineProperty を使用してデータ ハイジャックを実装し、最後に計算の最終結果を _data に渡します。

2.3 オブジェクト監視関連API Vue.set

まずは静的ページに移動しましょう。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>データの更新</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<本文>
    <div id="ルート">
       <h2>学校名: {{name}}</h2>
       <h2>学校の住​​所: {{address}}</h2>
       <hr/>
       <h2>生徒名: {{student.name}}</h2>
       <h2>学生の性別: {{student.sex}}</h2>
       <h2>生徒の年齢: {{student.age}}</h2>
       <h1>友達</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        定数vm = 新しいVue({
            el: '#root',
            データ:{
                名前:'MIT',
                住所:'UUU',
                学生:
                    名前: 'トム',
                    年齢: 20,
                    友達:
                        {名前: 'ジャック'、年齢: 21}、
                        {名前: 'メアリー'、年齢: 20}
                    ]
                },
            }
        })
    </スクリプト>
</本文>
</html>

テンプレート内の性別が実際にはデータ内に存在しないことに気付くかもしれません。オブジェクトに存在しないプロパティ値を出力すると undefined が出力されますが、Vue は処理後に undefined 値を表示しません。つまり、この時点ではページ上の生徒の性別の後に空の値があり、コンソールにエラーは表示されません。さて、要件を提示しましょう。学生に新しい性別属性を追加する必要がある場合。要件は、データ内の既存のデータを変更できないこと、つまり、性別をデータに直接入力できないことです。

先ほど紹介した _data によれば、_data を通じて sex 属性を直接バインドすることができます。すると、ページ上にレンダリングされていないことがわかりました。実際、これは 2.1 の問題と多少似ています。分析は以下の通りです。

まず、vm の _data を印刷してみましょう。下の図からわかるように、性別属性名と不明な属性値が学生オブジェクトに正常に追加されています。しかし、さらに下を見ていくと、その下にある多くの get メソッドと set メソッドの中に sex のメソッドがありません。これは、後から追加されたこの属性がレスポンシブなデータになっていないことを意味します。

この問題を解決するには、Vue が提供する API、つまり Vue.set() メソッドを使用して、後から追加されたデータもレスポンシブなデータになるようにします。このメソッドの最初のパラメータ target はデータの追加先を示し、2 番目のパラメータ key は追加する属性の名前を示し、3 番目のパラメータは属性値を示します。

この時点で、Vue.set(vm._data.student,'sex','unknown') を使用して、_data 内のプロパティを変更するだけでなく、ページ上のデータの監視と変更も行うことができます。同じ効果を持つ API として vm.$set もあります。これは Vue.set() と同じ使用方法と同じパラメータを持ち、vm.$set(vm._data.student,'sex','unknown') となります。この 2 つのメソッドで追加されたデータはレスポンシブ データになります。

関数を実装したら、省略形の最適化を検討できます。データ プロキシにより、vm.student === vm._data.student となるため、Vue.set(vm.student,'sex','unknown) と省略できます。

次に、実際のコーディングでブラウザでアイデアを検証します。ボタンをクリックして sex 属性を追加するとします。ここでは詳しく説明せず、コードを貼り付けるだけにします。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>データの更新</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<本文>
    <div id="ルート">
       <button @click="addSex">ボタンをクリックして新しい性別を追加します</button>
       <h2>学校名: {{name}}</h2>
       <h2>学校の住​​所: {{address}}</h2>
       <hr/>
       <h2>生徒名: {{student.name}}</h2>
       <h2>学生の性別: {{student.sex}}</h2>
       <h2>生徒の年齢: {{student.age}}</h2>
       <h1>友達</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        定数vm = 新しいVue({
            el: '#root',
            データ:{
                名前:'MIT',
                住所:'UUU',
                学生:
                    名前: 'トム',
                    年齢: 20,
                    友達:
                        {名前: 'ジャック'、年齢: 21}、
                        {名前: 'メアリー'、年齢: 20}
                    ]
                },
            },
            方法:{
                セックスを追加します(){
                    // メソッドでは、これは vm インスタンス オブジェクトを参照します // これは this.$set(this.student, 'sex', '男') と記述することもできます
                    Vue.set(this.student, '性別', '男性')
                }
            }
        })
    </スクリプト>
</本文>
</html>

ただし、この方法には一定の制限があります。追加が成功すると、データ内の学生オブジェクトに設定されます。データオブジェクトに直接設定するとどうなるでしょうか。たとえば、ここに新しい属性「学校設立日時」を追加する必要があるとします。Vue.set メソッドを引き続き使用すると、エラーが報告されます。

このメソッドは、レスポンシブ オブジェクト (ここでは data.student など) に新しいプロパティを追加し、ビューの更新をトリガーするためにのみ使用できることがわかります。 Vue は通常の新しいプロパティを検出できないためです。

2.4 オブジェクトに複数の新しい値を割り当てる

Object.assign() や _.extend() などを使用して、既存のオブジェクトに複数の新しいプロパティを割り当てる必要がある場合があります。ただし、この方法でオブジェクトに追加された新しいプロパティでは更新はトリガーされません。したがって、この場合は、元のオブジェクトと、混合するオブジェクトのプロパティを使用して新しいオブジェクトを作成する必要があります。

たとえば、これまで存在しなかった身長と体重の属性を学生に追加したい場合は、次のようにします。

const add = {'高さ': 180, "重さ": 150}
this.student = Object.assign({},this.student, add)

元のオブジェクトと新しく追加されたオブジェクトが結合され、空の新しいオブジェクトに割り当てられ、それが this.student に割り当てられます。最後に、データをレスポンシブな方法で学生に追加できます。

3. 監視アレイ

それでも、まずは静的ページに移動します。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
    <メタ文字セット="UTF-8">
    <title>データの更新</title>
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<本文>
    <div id="ルート">
       <button @click="addSex">ボタンをクリックして新しい性別を追加します</button>
       <h2>学校名: {{name}}</h2>
       <h2>学校の住​​所: {{address}}</h2>
       <hr/>
       <h2>生徒名: {{student.name}}</h2>
       <h2>学生の性別: {{student.sex}}</h2>
       <h2>生徒の年齢: {{student.age}}</h2>
       <h1>友達</h1>
       <ul>
           <li v-for="(f,index) in student.friends" :key="index">
               {{f.name}} - {{f.age}}
           </li>
       </ul>
       <h1>趣味</h1>
       <ul>
           <li v-for="(h,index) in student.hobby" :key="index">
               {{h}}
           </li>
       </ul>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        定数vm = 新しいVue({
            el: '#root',
            データ:{
                名前:'MIT',
                住所:'UUU',
                学生:
                    名前: 'トム',
                    年齢: 20,
                    友達:
                        {名前: 'ジャック'、年齢: 21}、
                        {名前: 'メアリー'、年齢: 20}
                    ]、
                    趣味:['食べる'、'寝る'、'豆で遊ぶ']
                },
            },
            方法:{
                セックスを追加します(){
                    this.$set(this.student, '性別', '男性')
                }
            }
        })
    </スクリプト>
</本文>
</html>

コンソールに vm._data を出力すると、student の hobby 配列については、配列全体に get と set があり、応答性があることが明確にわかります。ただし、配列内の要素は応答せず、配列内でハングしたままになります。これが、2.1 で配列インデックスの添え字を再割り当てしようとしたときに、ページ上で応答がなかった理由です。 hobby 配列をオブジェクトに変更して再度印刷すると、オブジェクト内のプロパティがレスポンシブであることがわかります。このことから、Vue では、配列内の値をインデックスによって変更する場合、いくつかの特別なメソッドを使用する必要があることが大まかに推測できます。

その方法はどこから来たのでしょうか?これで、Vue のドキュメントに何が書かれているかがわかります。ドキュメントリストレンダリング->配列更新検出->変更方法のページに、次のような文があります。

Vue は監視対象の配列変更メソッドをラップし、ビューの更新もトリガーするようにします。ラップされたメソッドには次のものが含まれます。

プッシュ()ポップ()シフト()アンシフト()スプライス()ソート()リバース()

配列内のデータを変更したい場合は、上記の 7 つの方法を試してみるとよいでしょう。これらのメソッドを見ると、配列プロトタイプ チェーン上のメソッドを思い浮かべるかもしれませんが、ここではそれは完全に正しいわけではありません。 Vue はこれらのメソッドをラップしてカプセル化しています。Vue がこれらのメソッドを解析するとき、最初のステップは通常どおり Array プロトタイプ チェーン内のメソッドを呼び出すことであり、2 番目のステップはテンプレートを再解析することです。テンプレートを再解析した後、新しい仮想 DOM が生成されて新しい DOM と古い DOM が比較され、最終的にページが更新されるため、これらのメソッドを使用するとビューの更新がトリガーされる可能性があります。この考えは、vm._data.student.hobby.push === Array.prototype.push が false であるという事実によって確認できます。

例えば、ここで趣味の「豆を打つ」を「習い事」に変更したい場合、どうすればよいでしょうか。

これらの点に触れる前に、次のコードを使用しますが、レンダリングは正しく行われません。データを変更してもページは更新されません。

 this.student.hobby[2] = '勉強'

次に、2つの方法を試してみましょう。まずは、先ほど紹介した配列操作の方法を試してみましょう。

this.student.hobby.splice(2,3,'学習')

次に、先ほど紹介した Vue.set メソッドを試してみましょう。

ここで付け加えておきたいのは、2.3 でこのメソッドを使用してオブジェクトの値を変更する方法を紹介したとき、メソッドの 2 番目のパラメーターは属性名だったのに対し、このメソッドを配列で使用する場合、2 番目のパラメーターは配列内の要素の添え字である必要があるということです。

this.$set(this.student.hobby,2,'学習')

どちらの方法でも、hobby 内のデータを正常に変更し、ページに正常に更新できます。

一般的に、配列のインデックスで値を割り当てることはできません。ただし、配列自体の方向は変更できます。前の図で見たように、hobby 配列自体は応答性があり、get と set を通じて監視できます。たとえば、フィルターはビジネス ロジックで配列をフィルター処理するために使用されます。その後、データに既に存在する配列に割り当てられます。ここで、ドキュメント内の配列の置換の概要をもう一度見てみましょう。 filter、concat、slice などのメソッドは元の配列を変更せず、常に新しい配列を返します。mutation メソッドを使用すると、古い配列を新しい配列に置き換えることができます。

要約する

この記事はこれで終わりです。皆さんのお役に立てれば幸いです。また、123WORDPRESS.COM のその他のコンテンツにも注目していただければ幸いです。

以下もご興味があるかもしれません:
  • Vue でのルーティングパラメータの変更とメソッドの監視
  • Vueはルート監視とパラメータ監視を実装します
  • Vueで複数のパラメータを同時に監視する実装

<<:  HTML における if 判断の使用

>>:  Mysql のデッドロックの表示とデッドロックの除去の詳細な説明

推薦する

MySQL での Truncate の使用法の詳細な説明

序文:テーブルをクリアしたいときは、truncate ステートメントをよく使用します。ほとんどの場合...

Linux Zabbixカスタム監視およびアラーム実装プロセスの分析

ターゲットzabbix フロントエンド監視の iostat コマンドでデータの 1 つを表示します。...

MySQL クエリ キャッシュのグラフィカルな説明

目次1. 原則の概要クエリキャッシュシステム変数1. クエリキャッシュを持つ2. クエリキャッシュ制...

js を使用して過去 1 週間、1 か月、3 か月の時間を取得する簡単な例

目次過去1週間の時間を取得する過去1か月の時間を取得する過去3か月分を取得新しい Date() と ...

Linux で SSH 経由でリモート ファイルシステムをマウントする方法の詳細な説明

SSHFS の機能: FUSE(Linux向けの最高のユーザー空間ファイルシステムフレームワーク)を...

複数のパッケージソースから同時にパッケージをロードするようにnpmを設定する方法

目次1. ローカルストレージを構築する2. npmパッケージを作成し、プライベートリポジトリにアップ...

Nginx の負荷分散構成、ダウンタイム発生時の自動切り替えモード

厳密に言えば、nginx には負荷分散バックエンド ノードのヘルス チェック機能はありませんが、デフ...

Vue で動的なスタイルを実現するためのさまざまな方法のまとめ

目次1. 三項演算子の判定2. 動的に設定されるクラス3. 方法判定4. 配列バインディング5. e...

MySQL がタイムスタンプを使用するときにタイムゾーンの問題を無視できるのはなぜですか?

私はいつも、なぜMySQLデータベースのtimestampタイムゾーンの問題を無視できるのか疑問に思...

docker-maven-plugin の詳細な使用方法

目次Docker-Maven-プラグインMavenプラグインの自動デプロイメント手順1. ホストマシ...

JavaScript配列の重複排除のいくつかの方法についての詳細な説明

目次1.重複排除を設定する2. 重複を削除するには、2 回の for ループを使用します。 3. i...

CSS スタイルで一般的なグラフィック効果を示すサンプルコード

一般的な基本グラフィックと私が遭遇するいくつかの小さなアイコンについて簡単に説明します。以下は CS...

HTMLテキスト内のすべてのタグを置き換える方法

(?i) は大文字と小文字を区別しないことを意味します。大文字と小文字をすべて置き換えます。 htm...