Vueでデータを読み取るためにこれを悪用しないでください

Vueでデータを読み取るためにこれを悪用しないでください

序文

Vue では、データ オプションは便利です。データを入力すると、Vue コンポーネント内のどこからでもこれを介してデータ内のデータを読み取ることができます。しかし、これを悪用してデータ内のデータを読み取ることは避けなければなりません。このコラムでは、どこで悪用を避けるべきか、また悪用するとどのような結果になるかを説明します。

1. これを使用してデータ内のデータを読み取るプロセス

Vue ソースコードでは、データをレスポンシブなものに変換するために、getter 関数と setter 関数がデータに追加されます。ゲッター関数のコードは次のとおりです。

関数reactiveGetter() {
 var value = getter ? getter.call(obj): val;
 if (依存ターゲット) {
  依存関係
  if (childOb) {
   childOb.dep.depend();
   Array.isArray(値)の場合{
    依存配列(値);
   }
  }
 }
 戻り値
}

これを使用してデータ内のデータを読み取ると、getter 関数がトリガーされ、 var value = getter ? getter.call(obj) : val;を使用して値が取得され、その後、 return valueが実行されて、データ読み取りの目的が達成されます。

ここで、Dep.target が存在する場合、これを使用してデータ内のデータを読み取ると依存関係が収集されるという結論を導き出すことができます。これを悪用してデータ内のデータを読み取ると、依存関係が繰り返し収集され、パフォーマンスの問題が発生します。

2. Dep.target はいつ存在しますか?

Dep.target は依存関係によって割り当てられます。依存関係はウォッチャーまたはサブスクライバーとも呼ばれます。 Vue には 3 種類の依存関係があり、そのうち 2 つは watch (リスナー) と computed (計算プロパティ) という非常に一般的な依存関係です。テンプレートの最初のレンダリング中に作成される、非表示の依存関係であるレンダリング ウォッチャーもあります。

Dep.target は依存関係の作成時に割り当てられ、依存関係はコンストラクター Watcher を使用して作成されます。

関数 Watcher(vm, expOrFn, cb, オプション, isRenderWatcher) {
 //...
 if (typeof expOrFn === 'function') {
  this.getter = expOrFn;
 } それ以外 {
  this.getter = parsePath(expOrFn);
 }
 this.value = this.lazy ? 未定義: this.get();
};
Watcher.prototype.get = 関数 get() {
 pushTarget(これを);
 試す {
  値 = this.getter.call(vm, vm);
 } キャッチ (e) {
  
 }
 戻り値
};
依存関係ターゲット = null;
var ターゲットスタック = [];
関数pushTarget(ターゲット) {
 ターゲットスタックをプッシュします。
 依存関係ターゲット = ターゲット;
}

コンストラクタ Watcher では、最後にインスタンス メソッドgetが実行され、インスタンス メソッドget内で Dep.target のpushTarget(this)への割り当てが実行されます。

依存関係は Vue ページまたはコンポーネントが最初にレンダリングされるときに作成されるため、パフォーマンスの問題は最初のレンダリングが遅いという問題になります。

3. データ内のデータを読み取るためにこれを悪用する場所

Dep.target が存在する場合、 this を悪用してデータ内のデータを読み取るこれらのコードを実行するとパフォーマンスの問題が発生するため、これらのコードが実行される場所にも書き込まれている場所を特定する必要があります。言い換えると、 this を悪用してデータ内のデータを読み取るとパフォーマンスの問題が発生する場所を特定する必要があります。

2 番目のセクションでは、Depth.target が割り当てられた後にvalue = this.getter.call(vm, vm)実行されることが紹介されています。ここで、 this.getterは関数です。これを使用してデータを読み取ると、依存関係が収集されます。乱用すると、パフォーマンスの問題が発生します。

this.getterは依存関係の作成プロセス中に割り当てられ、依存関係ごとにthis.getterが異なります。以下、一つずつ紹介させていただきます。

  • watch (リスナー) が依存するthis.getter parsePath関数であり、その関数パラメータはリッスンされるオブジェクトです。
var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]"));
関数parsePath(パス) {
 if (bailRE.test(path)) {
  戻る
 }
 var セグメント = path.split('.');
 関数(obj)を返す{
  (var i = 0; i < セグメントの長さ; i++) {
   もし (!obj) {
    戻る
   }
   obj = obj[セグメント[i]];
  }
  オブジェクトを返す
 }
}

以下のコードでは、 parsePath関数にaabcをパラメータとして渡すと、 this.getterに割り当てられた関数が返されます。 this.getter.call(vm, vm)を実行すると、 this.athis.abcの値が取得されます。このプロセスでは、データ内のデータを読み取るためにこれが悪用されるシナリオは発生しません。

時計:{
 a:function(新しい値、古い値){
 //何かする}
}
vm.$watch('abc', 関数(newVal, oldVal) {
 // 何かする })
  • 計算されたプロパティが依存するthis.getterには 2 つの種類があります。計算されたプロパティの値が関数である場合、 this.getterはこの関数です。計算されたプロパティの値がオブジェクトの場合、 this.getterこのオブジェクトの get プロパティ値となり、get プロパティ値も関数になります。この関数では、これを悪用してデータ内のデータを読み取るシナリオが発生する可能性があります。たとえば、コードは次のようになります。
計算:{
 d:関数(){
  結果を 0 にします。
  for(let key in this.a){
   if(this.a[キー].num > 20){
    結果 += this.a[キー].num + this.b + this.c;
   }それ以外{
    結果 += this.a[key].num + this.e + this.f;
   }
  }
  結果を返します。
 }
}

計算されたプロパティ d では、これを悪用してデータを読み取ることがあります。ここで、 this.aは配列です。このとき、 Dep.target の値は、計算されたプロパティ d の依存関係です。ループthis.aでは、 this を使用して a、b、c、e、f のデータを取得し、これらのデータに対して一連の複雑な論理演算を実行して、計算されたプロパティ d の依存関係を繰り返し収集します。これにより、計算されたプロパティ d の値を取得するプロセスが遅くなり、パフォーマンスの問題が発生します。

  • レンダリング Watcher のthis.getterは次の関数です。
更新コンポーネント = 関数() {
 vm._update(vm._render(), ハイドレーション);
};

その中で、 vm._render()は、テンプレート template によって生成されたレンダリング関数 render を仮想DOM(VNode):vnode = render.call(vm._renderProxy, vm.$createElement); 、レンダリング関数 render がどのようなものかを説明するために例を見てみましょう。

たとえば、テンプレート テンプレート:

<テンプレート>
 <div class="wrap">
 <p>{{a} } <span>{{b} } </span></p>
 </div>
</テンプレート>

レンダリング関数 render は、以下に示すように、vue-loader によって生成されます。

(関数匿名() {
 (これ) {
  _c('div', { を返す
   属性: {
    "クラス": "ラップ"
   }
  }, [_c('p', [_v(_s(a)), _c('span', [_v(_s(b))])])])
 }
})

with ステートメントの機能は、1 つまたは複数のステートメントのデフォルト オブジェクトを指定することです。たとえばwith(this){ a + b } this.a + this.bと同等です。次に、テンプレートで{{ a }}を使用することは、これを使用して data 内のデータ a を読み取ることと同等です。そのため、テンプレートによって生成されたレンダリング関数 render では、これを悪用して data 内のデータを読み取るシナリオが発生する可能性があります。たとえば、コードは次のようになります。

<テンプレート>
 <div class="wrap">
 <div v-for=リスト内の項目>
  <div> {{ arr[item.index]['name'] }} </div>
  <div> {{ obj[item.id]['年齢'] }} </div>
 </div>
 </div>
</テンプレート>

v-for を使用してリスト配列をループする場合、これを使用してデータ内の arr と obj のデータを読み取るため、これらのデータは一連の複雑な論理演算を実行してこの依存関係を繰り返し収集するため、初期レンダリングが遅くなり、パフォーマンスの問題が発生します。

4. データ内のデータを読み取るためにこれを悪用することを避ける方法

要約すると、これを悪用して計算プロパティとテンプレートのデータを読み取ると、依存関係のコレクションが繰り返し発生し、パフォーマンスの問題が発生します。この状況を回避するにはどうすればよいでしょうか?

  • 計算プロパティで回避する方法

これを回避するには、ES6 のオブジェクト分解割り当てを使用します。計算プロパティの値は、Vue のインスタンス化された this オブジェクトをパラメーターとする関数です。計算プロパティで this を悪用する上記の例では、次のように最適化できます。

最適化前:

計算:{
 d:関数(){
  結果を 0 にします。
  for(let key in this.a){
   if(this.a[キー].num > 20){
    結果 += this.a[キー].num + this.b + this.c;
   }それ以外{
    結果 += this.a[key].num + this.e + this.f;
   }
  }
  結果を返します。
 }
}

最適化後:

計算: {
 d({a,b,c,e,f}) {
 結果を 0 にします。
 for (let key in a) {
  (a[キー].num > 20) の場合 {
  結果 += a[キー].num + b + c;
  } それ以外 {
  結果 += a[キー].num + e + f;
  }
 }
 結果を返します。
 }
}

上記では、構造化代入を使用して、データ内の a、b、c、e、f を対応する変数 a、b、c、e、f に事前に割り当てています。その後、データ内の対応するデータの依存関係コレクションをトリガーすることなく、計算プロパティ内のこれらの変数を介してデータにアクセスできます。この方法では、データ内のデータは this を使用して 1 回だけ読み取られ、依存関係の収集は 1 回だけトリガーされるため、依存関係の収集が繰り返されることによって発生するパフォーマンスの問題を回避できます。

  • テンプレートで回避する方法

v-for ループで使用するデータを事前に処理し、v-for ループ内で配列やオブジェクト型のデータを読み取らないようにします。上記のテンプレート内で this を悪用した例では、次のように最適化できます。

list、arr、obj がすべてサーバーから返されるデータであり、arr と obj がどのモジュールのレンダリングでも使用されていないと仮定すると、次のように最適化できます。

最適化前:

<テンプレート>
 <div class="wrap">
 <div v-for=リスト内の項目>
  <div> {{ arr[item.index]['name'] }} </div>
  <div> {{ obj[item.id]['年齢'] }} </div>
 </div>
 </div>
</テンプレート>

最適化後:

<テンプレート>
 <div class="wrap">
 <div v-for=listData 内の項目>
  <div{{item.name}} </div>
  <div>{{item.age} </div>
 </div>
 </div>
</テンプレート>
<スクリプト>
定数arr = [];
定数オブジェクト = {}
エクスポートデフォルト{
 データ() {
 戻る {
  リスト: [],
 }
 },
 計算: {
 listData: 関数 ({list}) {
  リスト.forEach(項目 => {
  アイテム名 = arr[アイテムのインデックス].名前;
  アイテムの年齢 = obj[アイテムのID].年齢;
  })
  リストを返します。
 }
 },
}
</スクリプト>

上記は、Vue で this を悪用してデータ内のデータを読み取ることを避けるための詳細です。Vue で this を悪用することを避けるための詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Vue2.0/3.0双方向データバインディングの実装原理の詳細説明
  • vuexの強制リフレッシュによるデータ損失問題の分析
  • Vue はデータの変更をどのように追跡しますか?
  • Vue+canvas は、ウォーターフォール チャートを上から下までリアルタイムに更新する効果を実現します (QT と同様)
  • Vueデータ割り当て問題の解決
  • Vueはデータを初期状態にリセットします
  • Vue コンポーネント値転送中のデータ損失の分析と解決
  • SpringBoot+Vueでデータ追加機能を実現
  • 手書きの Vue2.0 データハイジャックの例
  • Vueデータ双方向バインディング実装方法
  • Vue でデータコレクターを設計する

<<:  AIX マウント NFS の書き込み効率が低い場合の解決策

>>:  Windows で MySQL 5.6 を 5.7 にアップグレードする方法

推薦する

Linux でパスワードを入力せずに sudo コマンドを実行する方法

sudo コマンドを使用すると、信頼できるユーザーは別のユーザー (デフォルトでは root ユーザ...

UDP シンプル サーバー クライアント コード例

UDP の理論については詳しく説明しません。UDP に関する HelloWorld プログラムを紹介...

MySQL 5.7 でルートパスワードを忘れた後に変更する方法の詳細なチュートリアル

序文長い間、MySQL のアプリケーションおよび学習環境は MySQL 5.6 以前のバージョンであ...

Javascriptのtry catchの2つの機能についてお話しましょう

プログラムは上から下へ順番に実行され、いくつかの制御文によって実行経路を変更することができます。制御...

JavaScriptカルーセルの実装について

今日もとても実践的な事例です。名前を聞くだけで高度で難しそうですよね?今日はカルーセル画像の真髄を簡...

JavaScript で円形カルーセルを実装する

この記事では、円形カルーセルを実装するためのJavaScriptの具体的なコードを参考までに紹介しま...

Centos7はMySQLログに基づいてデータを復元するためのサンプルコードを実装します

導入Binlog ログ、つまりバイナリ ログ ファイルは、データベースに対するユーザー操作の SQL...

CentOS 上の Docker に Jupyter をインストールしてポートを開く方法

目次jupyterをインストールするDocker ポートマッピングjupyterをインストールするp...

ウェブページの画像の回転を実現するjs

この記事では、Webページの画像の回転を実現するためのjsの具体的なコードを参考までに共有します。具...

Vueは時間カウントダウン機能を実装する

この記事では、Vueの具体的なコード例を参考までに紹介します。具体的な内容は以下のとおりです。必要:...

Linux シェル環境での Zabbix API の使用

Linux シェル環境で直接呼び出すことができます。公式 Web サイトによると、Zabbix のデ...

画像を使用してハイパーリンクのパーソナライズされた下線を実現します

画像内に下線付きのリンクが表示されても驚かないでください。実はとても簡単なので、あなたにもできるので...

SELINUXの動作原理の詳細な説明

1. はじめにSELinux が Linux にもたらす主な価値は、柔軟で構成可能な MAC メカニ...

MySQL のソートとページング (order by と limit) と既存の落とし穴

並べ替えクエリ (order by)電子商取引の場合: 今日完了したすべての注文を表示し、取引金額に...

MySQL のクラスター化インデックスとクラスター化インデックスの成長の仕組みを理解する

このノートでは、 MySQL の B+Tree インデックスとは何ですか?クラスター化インデックスは...