22 Vue 最適化のヒント (プロジェクトの実践)

22 Vue 最適化のヒント (プロジェクトの実践)

デモコードは Vue3 + ts + Vite で書かれていますが、Vue2 に適用できる最適化手法も記載します。Vue3 または Vue2 のみに適用できる最適化の場合は、タイトルにその旨を記載します。

コードの最適化

v-for でキーを使用する

v-for を使用してレンダリングされた要素リストを更新する場合、デフォルトのインプレース再利用戦略が使用されます。リスト データが変更されると、キー値に基づいて値が変更されたかどうかが判断されます。変更された場合は、この項目が再レンダリングされ、変更されていない場合は以前の要素が再利用されます。

キーの使用に関する注意事項:

  • 繰り返される可能性のあるキー値や変更される可能性のあるキー値は使用しないでください(コンソールにもリマインダーが表示されます)
  • 配列インデックスをキー値として使用しないでください。配列に要素を挿入すると、それ以降の要素のインデックスが変更されるためです。
  • 配列内に一意のキー値がない場合は、一意性を確保するために、値 Symbol() を持つキー フィールドを追加することを検討してください。

v-if/v-else-if/v-else でキーを使用する

おそらく多くの人がこの点を無視するでしょう。

理由: デフォルトでは、Vue は DOM を可能な限り効率的に更新します。つまり、同じタイプの要素を切り替えるときに、古い要素を削除して同じ場所に新しい要素を追加するのではなく、既存の要素にパッチを適用します。同一ではない要素が同一であると識別されると、予期しない副作用が発生する可能性があります。

v-if が 1 つだけあり、v-else または v-if-else がない場合、キーを追加する必要はありません。

v-for のキーと比較すると、v-if/v-else-if/v-else のキーは比較的単純です。固定の文字列または配列を直接記述できます。

  <遷移>
    <ボタン 
      v-if="編集中"
      v-on:click="isEditing = false"
    >
      保存
    </ボタン>
    <ボタン 
      v-else 
      v-on:click="isEditing = true"
    >
      編集
    </ボタン>
  </トランジション>
.v-enter-active、.v-leave-active {
  遷移: すべて 1;
}
.v-enter、.v-leave-to {
  不透明度: 0;
  変換: translateY(30px);
}
.v-アクティブ状態を解除{
  位置: 絶対;
}

たとえば、上記のコードでは、ボタンにトランジション効果が追加されているにもかかわらず、キースイッチが追加されていないとトランジションをトリガーできないことがわかります。
v-for と v-if を一緒に使用しないでください (Vue2)

この最適化手法は Vue2 に限定されています。Vue3 では v-for と v-if の優先順位が調整されています。

誰もが知っている

同じ要素に v-if と v-for を使用しないでください。 Vue 2.x スタイルガイドを参照してください

その理由は、v-for は v-if よりも優先度が高いため、同じタグで使用すると、まずそれぞれのレンダリングがループされ、その後条件判定が行われるからです。

注: Vue3 では v-if は v-for よりも優先度が高いため、v-for と v-if を一緒に使用すると、その効果は Vue2 で v-if を発生させた場合と同様になります。

例えば、次のコードはVue2では推奨されておらず、Vueも対応する警告を発します。

<ul>
  <li v-for="users 内の user" v-if="user.active">
    {{ユーザー名}}
  </li>
</ul>

v-ifを上位レベルに移動するか、計算プロパティを使用してデータを処理する必要があります。

<ul v-if="アクティブ">
  <li v-for="users 内の user">
    {{ユーザー名}}
  </li>
</ul>

ループに不要な親コンテナーを持たせたくない場合は、親要素として template を使用できます。Template は、ブラウザーによって DOM ノードとしてレンダリングされません。トラバーサル オブジェクト内の各項目の内容を判別してレンダリングされたデータを選択する場合は、computed を使用してトラバーサル オブジェクトをフィルターできます。

// js
usersActive = computed(()=>users.filter(user => user.active)) とします。

// テンプレート
<ul>
    <li v-for="usersActive 内のユーザー">
      {{ユーザー名}}
    </li>
</ul>

v-ifとv-showの適切な選択

v-if と v-show の違いは誰もがよく知っています。v-if は DOM の削除と追加を直接操作して要素の表示と非表示を制御します。v-show は DOM の表示 CSS を制御することで要素の表示と非表示を制御します。DOM の追加/削除操作のパフォーマンスは DOM の CSS プロパティを操作するパフォーマンスよりもはるかに低いため、要素を頻繁に表示/非表示にする必要がある場合は、v-show を使用してパフォーマンスを向上させます。
要素を頻繁に表示/非表示にする必要がない場合は、v-if を使用して DOM を削除し、ブラウザーが DOM のこの部分をレンダリングするために必要なリソースを節約できます。

単純な計算プロパティの使用

複雑な計算プロパティは、できるだけ多くの単純なプロパティに分割する必要があります。

各計算プロパティが依存関係の少ない非常に単純な式で構成されている場合、正しく動作することを確認するためのテストを記述するのが簡単になります。

読みやすくするために、簡略化された計算プロパティでは、再利用できない場合でも、各値にわかりやすい名前を付ける必要があります。これにより、他の開発者 (および将来のあなた) が関心のあるコードに集中し、何が起こっているかを把握しやすくなります。

「変化を受け入れる」方が良い
名前を付けることができる任意の値をビューで使用できます。たとえば、ユーザーにいくら節約できたかを伝えるメッセージを表示したい場合があります。また、税金を計算したい場合もありますが、合計金額の一部としてではなく、個別に表示したい場合もあります。
小さく焦点を絞った計算プロパティにより、情報を使用する際に必要な仮定が減るため、要件が変更されたときに必要なリファクタリングが少なくなります。

Vue2スタイルガイドを参照してください

Computed は誰もがよく知っているものです。Computed は、その表現で依存する反応データが変化すると再計算されます。計算プロパティにさらに複雑な式を記述すると、依存するリアクティブ データの数も任意に増加します。依存関係のいずれかが変更されると、式全体を再計算する必要があります。

価格 = 計算される(()=>{
  基本価格 = 製造コスト / (1 - 利益マージン) とします。
  戻る (
      基本価格 -
      基本価格 * (割引率 || 0)
  )
})

製造コスト、利益マージン、または割引パーセントのいずれかが変更されると、全体の価格が再計算されます。
しかし、これを次のように変更すると

basePrice = computed(() => manufacturingCost / (1 - profitMargin)) とします。
割引率を計算します(() => 基本価格 * (割引率 || 0))
finalPrice = computed(() => basePrice - discount) とします。

discountPercent が変更された場合、discount と finalPrice のみが再計算されます。computed のキャッシュ機能により、basePrice は再計算されません。

機能コンポーネント (Vue2)

これは Vue2 での最適化のみであり、3.x ではステートフル コンポーネントと関数型コンポーネント間のパフォーマンスの違いが大幅に削減され、ほとんどのユース ケースでは無視できるほどになっていることに注意してください。したがって、SFC で functional を使用する開発者の移行パスは、その属性を削除し、props のすべての参照を $props に、attrs のすべての参照を $attrs に名前変更することです。

最適化前

<テンプレート> 
    <div class="cell"> 
        <div v-if="値" クラス="on"></div> 
        <セクション v-else クラス="オフ"></セクション> 
    </div> 
</テンプレート>

<スクリプト> 
エクスポートデフォルト{ 
    プロパティ: ['値'], 
} 
</スクリプト>

最適化後

<テンプレート機能> 
    <div class="cell"> 
        <div v-if="props.value" class="on"></div> 
        <セクション v-else クラス="オフ"></セクション> 
    </div> 
</テンプレート>

<スクリプト> 
エクスポートデフォルト{ 
    プロパティ: ['値'], 
} 
</スクリプト>

  • いいえこれ(インスタンスなし)
  • 応答データなし

コンポーネントを分割する

何?あなたが書いた Vue ファイルに 1,000 行を超えるコードがありますか? 🤔
適切なコンポーネント分割は、パフォーマンスを最適化できるだけでなく、コードをより明確で読みやすくすることもできます。単一機能原理

出典: https://slides.com/akryum/vueconfus-2019#/4/0/3

最適化前

<テンプレート>
  <div :style="{ 不透明度: 数値 / 300 }">
    <div>{{ 重い() }}
  </div>
</テンプレート>

<スクリプト>
エクスポートデフォルト{
  プロパティ: ['数値'],
  メソッド: {
    重い () { /* 重いタスク */ }
  }
}
</スクリプト>

最適化後

<テンプレート>
  <div :style="{ 不透明度: 数値 / 300 }">
    <チャイルドコンプ/>
  </div>
</テンプレート>

<スクリプト>
エクスポートデフォルト{
  プロパティ: ['数値'],
  コンポーネント:
    チャイルドコンプ: {
      メソッド: {
        重い () { /* 重いタスク */ }
      },
      レンダリング (h) {
        h('div', this.heavy()) を返します
      }
    }
  }
}
</スクリプト>

Vue はコンポーネント レベルで更新されるため、各フレームでデータの変更によって親コンポーネントが再レンダリングされますが、ChildComp は内部に応答的なデータ変更がないため再レンダリングされません。最適化されたコンポーネントは、レンダリングごとに時間のかかるタスクを実行しません。

ローカル変数の使用

最適化前

<テンプレート>
  <div :style="{ opacity: start / 300 }">{{ 結果 }}</div>
</テンプレート>

<スクリプト>
'@/utils' から {heavy} をインポートします

エクスポートデフォルト{
  プロパティ: ['開始'],
  計算: {
    基数(){戻り値42}、
    結果 () {
      結果 = this.start とする
      (i = 0; i < 1000; i++ とします) {
        結果 += 重い(this.base)
      }
      結果を返す
    }
  }
}
</スクリプト>

最適化後

<テンプレート>
  <div :style="{ 不透明度: 開始 / 300 }">
    {{ 結果 }}
</テンプレート>

<スクリプト>
'@/utils' から {heavy} をインポートします

エクスポートデフォルト{
  プロパティ: ['開始'],
  計算: {
    基数(){戻り値42}、
    結果 () {
      定数ベース = this.base
      結果 = this.start とする
      (i = 0; i < 1000; i++ とします) {
        結果 += 重い(ベース)
      }
      結果を返す
    }
  }
}
</スクリプト>

ここでの主な違いは、最適化前後のコンポーネントの計算されたプロパティ結果の実装の違いです。最適化前のコンポーネントは計算プロセス中に this.base に何度もアクセスしますが、最適化後のコンポーネントは計算前にローカル変数 base を使用し、this.base をキャッシュしてから、直接 base にアクセスします。

では、なぜこの違いがパフォーマンスの違いを引き起こすのでしょうか? その理由は、this.base にアクセスするたびに、this.base はレスポンシブ オブジェクトであるため、そのゲッターがトリガーされ、依存関係コレクションに関連するロジック コードが実行されるためです。同様のロジックが頻繁に実行されると、例のように、数百のコンポーネントが数百サイクルで更新され、各コンポーネントの計算が再計算されるようにトリガーされ、依存関係の収集に関連するロジックが複数回実行されるため、パフォーマンスは当然低下します。

需要の観点から言えば、this.base は依存関係の収集を 1 回実行し、そのゲッター評価結果をローカル変数 base に返すだけで十分です。後で base に再度アクセスすると、ゲッターがトリガーされず、依存関係の収集ロジックが実行されないため、パフォーマンスが自然に向上します。

Vue.js のパフォーマンス最適化のヒント 9 つを公開

KeepAliveの使用

レンダリングコストの高いコンポーネントを頻繁に切り替える必要がある場合は、キープアライブを使用してコンポーネントをキャッシュできます。キープアライブを使用すると、キープアライブでラップされたコンポーネントの vnode と DOM は、最初のレンダリング後にキャッシュされます。次回コンポーネントが再度レンダリングされるときに、対応する vnode と DOM がキャッシュから直接取得され、レンダリングされます。コンポーネントの初期化、レンダリング、パッチ適用などの一連のプロセスを再度実行する必要がないため、スクリプトの実行時間が短縮され、パフォーマンスが向上します。

注意: キープアライブを乱用すると、長時間にわたって大量のメモリを占有するため、アプリの動作が遅くなるだけです。

イベントの破壊

コンポーネントが破棄されるときは、メモリ リークを防ぐために、コンポーネントに追加されたグローバル イベントとタイマーをクリアする必要があります。
Vue3のHOOKを使用すると、イベントの宣言と破棄を一緒に記述できるため、読みやすくなります。

関数 scrollFun(){ /* ... */
document.addEventListener("スクロール", scrollFun)

マウント解除前に(()=>{
  document.removeEventListener("スクロール", scrollFun)
})

Vue2 では、$once を通じてこの効果を実現できます。もちろん、optionsAPI beforeDestroy でイベントを破棄することもできますが、後者では同じ機能を持つコードが分散されるため、前者の方法をお勧めします。

関数 scrollFun(){ /* ... */
document.addEventListener("スクロール", scrollFun)

this.$once('hook:beforeDestroy', ()=>{
  document.removeEventListener("スクロール", scrollFun)
})

関数 scrollFun(){ /* ... */

エクスポートデフォルト{
  作成された() {
    document.addEventListener("スクロール", scrollFun)
  },
  beforeDestroy(){
    document.removeEventListener("スクロール", scrollFun)
  }
}

画像の読み込み

画像の遅延読み込み: ページ上に多くの画像があり、すべての画像が1つの画面に表示されない状況に適しています。vue-lazyloadプラグインは、非常に便利な画像遅延読み込み命令v-lazyを提供します。

ただし、すべての画像が遅延読み込みに適しているわけではありません。たとえば、バナーやフォトアルバムの場合は、現在表示されている画像の前後の画像を優先的にダウンロードするために、画像のプリロード技術を使用することをお勧めします。

合理的なデータ処理アルゴリズムを使用する

これは、データ構造とアルゴリズムに関する知識を比較的テストするものです。たとえば、配列を多階層構造に変換する方法などです。

/**
 * 配列からツリー構造へ、時間計算量 O(n)
 * @param list 配列 * @param idKey 要素 ID キー * @param parIdKey 要素の親 ID キー * @param parId 最初のレベルのルート ノードの親 ID 値 * @return {[]}
 */
関数 listToTree (リスト、idKey、parIdKey、parId) {
    map = {} とします。
    結果 = [] とします。
    len = list.length;とします。

    // マップを構築する
    (i = 0; i < len; i++) の場合 {
        //配列内のデータをキーと値のペアの構造に変換します (ここでの配列と obj は相互参照します。これがアルゴリズム実装の重要なポイントです)
        map[リスト[i][idKey]] = リスト[i];
    }

    //ツリー配列を構築 for(let i=0; i < len; i++) {
        itemParId = list[i][parIdKey]とします。
        // トップレベルノード if(itemParId === parId) {
            結果をプッシュします(リスト[i]);
            続く;
        }
        // 孤立ノード、破棄(親ノードが存在しない)
        if(!map[itemParId]){
            続く;
        }
        // 現在のノードを親ノードの子に挿入します (参照データ型であるため、obj 内のノードが変更されると、結果内の対応するノードもそれに応じて変更されます)
        if(map[itemParId].children) {
            map[itemParId].children.push(リスト[i]);
        } それ以外 {
            map[itemParId].children = [list[i]];
        }
    }
    結果を返します。
}

他の

上記の方法以外にも最適化テクニックはたくさんありますが、私のプロジェクトではあまり使用していません🤣

  • オブジェクトをフリーズする(不要な応答データが応答しないようにするため)
  • 長いリストのレンダリング - バッチレンダリング
  • 長いリストのレンダリング - 動的レンダリング (vue-virtual-scroller)
  • ...

ファーストスクリーン/ボリュームの最適化

私のプロジェクトでは、最初の画面の最適化のために主に次の最適化の方向性を持っています。

  • 音量
  • コード分​​割
  • ネットワーク

ボリュームの最適化

  • バンドルされたコードを圧縮する: webpackとviteのプロダクション環境パッケージングでは、デフォルトでコードを圧縮します。通常、特別な処理は必要ありません。webpackは、対応する圧縮プラグインを介して手動で実装することもできます。
  • ソースマップをキャンセル: パッケージ化された製品に .map ファイルがあるかどうかを確認できます。ある場合は、ソースマップの値を false または空に設定して、コード マッピングをオフにすることができます (これは多くのスペースを消費します)
  • パッケージ化のために gizp 圧縮を有効にする: これには、サーバー側で gizp 転送も有効にする必要があります。有効にしないと、無効になります (webpack には対応する gzip 圧縮プラグインがあります。webpack 圧縮プラグインのバージョンによって異なる場合があるため、最初に公式 Web サイトを確認することをお勧めします)

コード分​​割

コード分​​割の役割は、パッケージ化された製品を esModule に依存する小さな製品に 1 つずつ分割することです。したがって、import() 関数を使用してファイルまたは依存関係をインポートすると、ファイルまたは依存関係は小さな製品として個別にパッケージ化されます。 ルートの遅延読み込みと非同期コンポーネントの両方でこの原則が使用されます。

  • ルーティングの遅延読み込み
  • 非同期コンポーネント

UI ライブラリの場合、通常はオンデマンドのコンポーネント読み込みは使用せず、CDN 経由で導入して最適化することを好みます。

ネットワーク

  • CDN: まず、前述の通りCDNを導入します。開発フェーズではローカルライブラリが使用され、パッケージ化の際に外部拡張機能(Externals)を構成することでこれらの依存関係が除外されます。次に、CDNを介してHTMLファイルにインポートします。
  • サーバー プッシュ: HTTP2 は比較的成熟しており、前述の CDN の導入後、Web サイトで HTTP2 のサーバー プッシュ機能を使用して、ブラウザーがこれらの CDN やその他のファイルを事前に読み込むことができるようになりました。
  • gzip を有効にする: これは上で説明しました。原理としては、クライアントとサーバーの両方が gzip 転送をサポートしている場合、サーバーは gzip で圧縮されたファイルを優先的に送信し、クライアントはそれを受信した後に解凍します。
  • キャッシュを有効にする: 通常はネゴシエートされたキャッシュを使用しますが、これはすべての状況に当てはまるわけではありません。たとえば、サーバー プッシュを使用するファイルの場合、ファイル名を自由に変更することはできません。だから私は通常、生成されたメインファイルのファイル名を修正します

これで、22 の Vue 最適化テクニック (プロジェクトに実用的) に関するこの記事は終了です。より関連性の高い Vue 最適化テクニックについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vueプロジェクトでlessを使用するためのヒント
  • Vue.js パフォーマンス最適化 N 個のヒント (収集する価値あり)
  • Vueプロジェクトでよく使われる実践的なスキルのまとめ
  • Vue Router の 10 の高度なヒントのまとめ
  • 読めばわかるVueの8つのヒント
  • Vue 要素と Nuxt の使用に関するヒントを共有する
  • Vue 開発における一般的なルーチンとテクニックの概要
  • Vue関数コンポーネントの使用に関する簡単な説明
  • Vue.js でより良い v-for ループを書くための 6 つのヒント
  • 知っておくべき 25 の Vue のヒント

<<:  C++ を使用して MySQL に接続する方法

>>:  Win10 + Ubuntu20.04 LTS デュアルシステムブートインターフェースの美化

推薦する

行の高さと垂直方向の配置についての深い理解

いくつかの概念行ボックス: インライン ボックスを囲むボックス。1 つ以上の行ボックスが積み重ねられ...

Windows 版 MySQL のインストール、起動、基本設定に関する詳細なグラフィック チュートリアル

ダウンロード:ステップ 1: ウェブサイトを開きます (ダウンロードするには公式ウェブサイトにアクセ...

WindowsでMysql5.7.17のインストールと起動に失敗する問題を解決する

マシンに初めて MySQL をインストールします。オペレーティングシステムはwin7ですmysqlの...

HTMLからPDFへのスクリーンショット保存機能の実装

テクノロジーの活用itext.jar: バイト ファイル入力ストリームを画像、PDF などに変換しま...

MySQLクエリの冗長インデックスと未使用のインデックス操作

MySQL 5.7 以降のバージョンでは、冗長インデックス、重複インデックス、およびインデックスを使...

MySQL テーブル自動増分 ID オーバーフロー障害レビュー ソリューション

問題: MySQLテーブル内の自動増分IDのオーバーフローによりビジネスブロックが発生した背景: t...

Mysql の一般的なベンチマーク コマンドの概要

mysqlslap共通パラメータの説明–auto-generate-sql システムはテスト用のSQ...

Vue で親子コンポーネントの値を双方向バインドするために v-model を使用するときに発生する問題と解決策

目次シナリオ解決してみる解決するシナリオ今日、コンポーネントの双方向データバインディングにv-mod...

Alibaba Cloud Server Linux システムは Tomcat を構築して Web プロジェクトを展開します

私は全体のプロセスを 4 つのステップに分けます。 JDKをダウンロードしてインストールするTomc...

見落としがちなVue.jsのAPIを詳しく解説

目次次のチェックv-model 構文シュガー.sync 修飾子$セット計算プロパティセット要約する次...

Docker を使用して開発環境を構築する方法 (Windows および Mac)

目次1. Dockerを使用する利点2. Dockerをインストールする1) LinuxにDocke...

MySQLのMVCCマルチバージョン同時実行制御の実装

1 MVCCとは何かMVCC の正式名称は、マルチバージョン同時実行制御です。データベースへの同時ア...

CentOS7.4 に MySQL 5.7.26 をインストールするための詳細なチュートリアル

CentOS にはデフォルトで MariaDB がインストールされていますが、これは MySQL の...

Linux での wget コマンドの基本的な使い方

目次序文1. wgetを使用して単一のファイルをダウンロードする2. wget -Oを使用してダウン...

CSS ファイルをインポートする 4 つの方法 (インライン、インライン、外部、インポート) の詳細な説明

CSS インポート方法 - インラインスタイルタグ属性を通じて、CSSのキーと値のペアがタグに直接書...