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 のデッドロックの表示とデッドロックの除去の詳細な説明

推薦する

JavaScript axiosのインストールとパッケージ化のケースの詳細な説明

1. axiosプラグインをダウンロードする cnpm インストール axios -S 2. mai...

docker version es、milvus、minio 起動コマンドの詳細な説明

1. es起動コマンド: docker run -itd -e TAKE_FILE_OWNERSHI...

LAMP ソースコードを使用したエンタープライズレベルのインストールチュートリアル

目次LAMPアーキテクチャ1.ランプの紹介2. WebサービスワークフローWebサーバーのリソースは...

ブラウザでのjsのイベントループイベントキューの詳細な説明

目次序文スタックと2つのキューを理解する実行プロセス簡単な例より難しい例要約する序文以下の内容はブラ...

単一マシン上での Tomcat の複数インスタンスの実装

1. はじめにまず、1 台のマシンで複数のインスタンスを使用する理由という質問に答える必要があります...

フロントエンドJavaScriptのクラス

目次1. クラス1.1 コンストラクタ() 1.2 ゲッターとセッター1.3 これ1.4 静的プロパ...

Linux システムでの CPU 使用率が高い場合のトラブルシューティングのアイデアと解決策

序文Linux 運用保守エンジニアとして、日々の業務の中で Linux サーバーの CPU 負荷が ...

React useEffect の理解と使用

目次繰り返しレンダリングループを避ける副作用の除去についてReact16.8 の新しい useEff...

熟練デザイナーの7つの原則(2):色の使い方

<br />前回の記事:優秀なデザイナーの7つの原則(1):フォントデザイン 英語 原文...

MySQL テーブルスペースのリカバリに対する正しいアプローチについての簡単な説明

目次予備的注釈問題の再現データ削除の原則データの再利用どの操作がデータホールの原因になりますか?表領...

面接官がmysqlのcharとvarcharの違いを尋ねたとき

目次charとvarcharの違いcharとvarcharの違い上記は、MySQL における cha...

Linuxでmore、less、catコマンドを使用してファイルの内容を表示します

Linux では、cat、more、less の各コマンドを使用してファイルの内容を表示できます。c...

MySQL 8.0.19 のインストールと設定方法のグラフィックチュートリアル

この記事は、参考のためにMySQL 8.0.19のインストールと設定のグラフィックチュートリアルを記...

HTML に埋め込まれた MP4 形式のビデオが再生できないのはなぜですか?

次のコードは、私の test.html にあります。ビデオは、c:\test.html などの絶対パ...

Web デザイン: Web ミュージックの実装テクニック

<br />Web ページに音楽を挿入する場合、サフィックスに応じて異なるコードを記述す...