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 にアップグレードする方法

推薦する

Reactコンポーネント通信の詳細な説明

目次コンポーネント通信の概要コンテンツ3つの方法まとめコンポーネントコミュニケーション - 父から息...

Linux サービスでファイアウォールを有効にする 2 つの方法

方法は2つあります: 1. サービス方法ファイアウォールのステータスを確認します。 [root@ce...

js での遅延読み込みとプリロードの具体的な使用法

遅延読み込み(レイジー読み込み)とプリロードは、Web 最適化によく使用される手段です。 。 1. ...

MySQL実行計画の詳細な説明

EXPLAIN ステートメントは、MySQL がステートメントを実行する方法に関する情報を提供します...

パスワードログインなしのLinux構成スタンドアロンおよびフルディストリビューションの詳細なチュートリアル

目次1: 単一マシンのパスワードフリーログイン構成1. 仮想マシンのホスト名を設定する2. 仮想マシ...

MySQLは1つのテーブルからデータをクエリし、それを別のテーブルに挿入する実装方法

MySQLは1つのテーブルからデータをクエリし、それを別のテーブルに挿入する実装方法ウェブサイト開発...

JavaScript スクリプトが実行されるタイミングの詳細な説明

JavaScript スクリプトは HTML 内のどこにでも埋め込むことができますが、いつ呼び出され...

CSS におけるスタックコンテキストの具体的な使用法

序文一部の CSS 相互作用の影響により、要素に設定されたz-index実際のサイズに応じて重ね合わ...

MySQL 接続制御プラグインの紹介

目次1. 接続制御プラグイン(connection_control)の紹介1.1 connectio...

MYSQL フルバックアップ、マスタースレーブレプリケーション、カスケードレプリケーション、および半同期の概要

MySQL フルバックアップ1. バイナリログを有効にし、データベースから分離して別々に保存する v...

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

最近、データベースについて学び始めました。最初にやったことは、データベースとは何か、データベースとデ...

今日と昨日の 0:00 タイムスタンプを取得する MySQL の例

以下のように表示されます。昨日: UNIX_TIMESTAMP(CAST(SYSDATE() AS ...

React HTML で react を使用する 2 つの方法

基本的な使い方 <!DOCTYPE html> <html lang="...

HTML ハイパーリンク スタイル (4 つの異なる状態) の設定例

コードをコピーコードは次のとおりです。 <スタイル タイプ="text/css&qu...

TypeScriptの列挙型を詳しく説明する

目次1. デジタル列挙2. 文字列の列挙3. 逆マッピング4. 異種列挙5. 定数列挙6. 列挙メン...