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 で一意の ID を生成するいくつかの方法

考えられる解決策1. Math.randomは[0,1)の範囲の乱数を生成します。 //今回は生成さ...

jQuery は、画像を切り替えるための左ボタンと右ボタンのクリックを実装します。

この記事では、左ボタンと右ボタンをクリックすることで画像を切り替えるjQueryの具体的なコードを例...

スライダー間隔コンポーネントのネイティブ js 実装

この記事の例では、スライダー間隔コンポーネントを実装するためのjsの具体的なコードを参考までに共有し...

Sitemesh チュートリアル - ページ装飾技術の原理と応用

1. 基本概念1. Sitemeshはページ装飾技術です。 1 : フィルターを通してページアクセス...

CentOS 8.1 で LEMP (Linux+Nginx+MySQL+PHP) 環境を構築する (チュートリアルの詳細)

目次ステップ1: CentOS 8でパッケージを更新するステップ2: CentOS 8にNginx ...

Reactの簡単な紹介

目次1. CDNの紹介1.1 react (最初にインポート) 1.2 react-dom(後ほど紹...

Kali Linux インストール VMware ツールのインストール プロセスと VM インストール vmtools ボタン グレー

Xiaobai は vmtools のインストールを記録します。 1. 意義と機能: VMWARE ...

1 つ以上の Linux インスタンスから SSH キー ペアのバインドを解除します。

キーペアの分離1 つ以上の Linux インスタンスから SSH キー ペアのバインドを解除します。...

Ubuntu の起動後にアプリケーションを実行するためのターミナルの設定方法

1.メニューバーにスタートと入力し、スタートアップアプリケーションをクリックして入力します。 2. ...

Docker stopはすべてのコンテナを停止/削除します

この記事では主に、すべてのコンテナを削除する Docker stop/remove を紹介し、皆さん...

MySQL 5.7.21 解凍バージョンのインストールと設定のグラフィックチュートリアル

この記事では、MySQL 5.7.21の解凍版をダウンロードしてインストールする詳細な手順を記録して...

MySQLで更新可能なビューを作成する方法の詳細な説明

この記事では、例を使用して、MySQL で更新可能なビューを作成する方法について説明します。ご参考ま...

Linux システムのデュアル ネットワーク カード バインディング構成の実装

システムバージョン [root@ ~]# cat /etc/redhat-release CentO...

VMware仮想マシンブリッジによるインターネット相互接続を実現する方法

VMware をインストールして新しい仮想マシンを作成したら、オプション バーの [編集] - [仮...

ウェブサイトでページコンテンツや情報を直接コピーできない問題を解決する方法

最近では、多くのウェブサイトでは、ページ上の特定のコンテンツや情報を直接コピーすることは許可されてお...