この記事はdevMeTokyoがパートナーシップを結んでいるVueStore FrontのFilip Rakowskiが寄稿した記事を翻訳しました。
https://itnext.io/vue-js-app-performance-optimization-part-1-introduction-to-performance-optimization-and-lazy-29e4ff101019
モバイルファーストアプローチは標準的なものになり、不確実なネットワークなどは常に考慮すべきものですが、アプリケーションの読み込みを高速な状態で維持することはますます困難になっています。この連載では、Vue Storefrontで使用しているVueパフォーマンス最適化手法、およびVue.jsアプリケーションを使用して即座にロードしてスムーズに実行できるようにする方法について詳しく説明します。私の目標は、このシリーズをVueアプリのパフォーマンスに関する完全なガイドにすることです。
パート1 - パフォーマンスの最適化とlazy loadの紹介
パート2 - ルートのlazy loadとベンダーバンドルのアンチパターン - まもなく
パート3 - Vuexストアと単一コンポーネントのlazy load - 近日中に
パート4 - ライブラリをlazy loadしてより小さな同等物を見つける - すぐに
パート5 - Service Workerのキャッシュを利用する - 間もなく
パート6 - プリフェッチ
Webpackのバンドルはどのように機能するの?
このシリーズのヒントの大部分は、JSバンドルを小さくすることに焦点を当てています。まず最初にWebpackがすべてのファイルをどのようにまとめているかを理解する必要があります。
私たちの資産をバンドルしている間、Webpackはディペンデンシーグラフと呼ばれるものを作成しています。インポートに基づいてすべてのファイルをリンクするグラフです。 webpack configのエントリポイントとしてmain.jsというファイルが指定されていると仮定すると、それが依存グラフのルートになります。このファイルにインポートするすべてのjsモジュールがグラフのリーフになり、このリーフにインポートされるすべてのモジュールがそれぞれのリーフになります。
Webpackはこの依存関係グラフを使用して、出力バンドルに含めるファイルを特定します。出力バンドルは、ディペンデンシーグラフのすべてのモジュールを含む単一の(または後の部分で説明するように複数の)JavaScriptファイルです。
このプロセスを以下のように説明できます。
バンドルがどのように機能するかがわかったら、プロジェクトが大きくなるほど初期のJavaScriptバンドルが大きくなることが明らかになります。大きければ大きいほどダウンロードして解析するのにかかる時間が長くなるため、ユーザーとって意味あるものが表示されるまでに時間がかかります。ユーザーを待たせれば待たせるほど彼らがウェブサイトを去ってしまい直帰率が高くなります。つまりバンドルが大きくなるとユーザーが少なくなります、ほとんどの場合。
Lady Loading
では新機能をアプリケーションに追加した場合どうやってバンドルサイズを削減できますか。答えは簡単です - Lazy LoadとCode Splittingを使います。
名前が示すように、lazy loadはアプリケーションの一部を遅延ロードすることです。言い換えれば - 本当に必要なときにだけそれらをロードするということです。Code Splittingは、単にこの遅延ロードされたチャンクにアプリを分割することです。
ほとんどの場合ユーザーがWebサイトにアクセスした直後に、Javascriptバンドルからすべてのコードを取得する必要はありません。たとえ1つのユーザーだけが必要であっても、ユーザーがどこに行くかに関係なく3つの異なるルートがユーザーに表示される場合でも、ユーザーは常にバンドル全てをダウンロードし、解析して実行する必要があります。なんていう時間とエネルギーの無駄遣いなんでしょう!
Lazy Loadingを使用すると、バンドルを分割して必要な部分だけを配信できるので、ユーザーは不要なコードをダウンロードして解析する時間を無駄に費やさなくなります。
当社のWebサイトで実際に使用されているJavaScriptコードの量を確認するには、開発ツール - > cmd + shift + p - >タイプカバレッジ - >「record」をクリックします。これで、ダウンロードしたコードのどれだけが実際に使用されたのかを確認できます。
赤でマークされたものはすべて現在のルートでは必要とされないもので、Lazy Loadすることができます。ソースマップを使用している場合は、このリスト内の任意のファイルをクリックして、どの部分が呼び出されていないかを確認できます。ご覧のとおり、vuejs.orgでさえも改善の余地があります。適切なコンポーネントとライブラリをLazy Loadすることで、Vue Storefrontのバンドルサイズを60%削減することができました。
さて、私たちはそれがどんなLazy Loadであるか、そしてそれがかなり便利であることを知っています;)
Vue.jsアプリケーションでそれをどのように使用できるかを見てみましょう。
動的インポート
アプリケーションの一部をwebpackの動的インポートでlazy loadすることは簡単にできます。それらがどのように機能し、通常のインポートとどう違うのかを見てみましょう。
このように標準的な方法でJSモジュールをインポートするとします。
// main.js
import ModuleA from './module_a.js'
ModuleA.doStuff()
これがディペンデンシーグラフの ModuleA のリーフとして追加され、一緒にバンドルされます。
しかしユーザーからの入力へ対するレスポンスなど、特定の状況下でのみ ModuleA が必要になる場合はどうなりますか?このモジュールを最初のバンドルにバンドルするのは、まったく必要ないかもしれないので悪い考えです。アプリケーションに対してこの大量のコードをダウンロードするタイミングを通知する方法が必要です。
これが動的インポートが私たちを助けてくれる場所です!この例を見てください。
//main.js
const getModuleA = () => import('./module_a.js')
// ユーザーの入力に対してのレスポンスするときに呼ばれる
getModuleA()
.then({ doStuff } => doStuff())
ここで何が起こったのかを簡単に見てみましょう。
module_a.jsを直接インポートする代わりに、import()を返す関数を作成しました。これで、webpackは動的にインポートされたモジュールのコンテンツを別のファイルにバンドルし、関数が呼び出されない限りインポートは呼び出されずファイルはダウンロードされません。コードの後半で、特定のユーザーインタラクション(ルート変更やクリックなど)に対する応答として、このオプションのコードのまとまりをダウンロードしました。
動的インポートを行うことで、依存関係グラフに追加されるリーフを基本的に切り取り、必要と判断したときにダウンロードされる別のリーフを作成します(つまり、module_a内にインポートされるモジュールも切り取ります)。
このメカニズムをよりよく説明する別の例を見てみましょう。
main.js、module_a.js、module_b.js、module_c.jsの4つのファイルがあるとしましょう。動的インポートがどのように機能するかを理解するために、mainとmodule_aのソースコードだけが必要です。
//main.js
import ModuleB from './mobile_b.js'
const getModuleA = () => import('./module_a.js')
getModuleA()
.then({ doStuff } => doStuff()
)
//module_a.js
import ModuleC from './module_c.js'
module_aを動的にインポートされるモジュールにすることで、依存関係グラフの一部をmodule_aとそのすべての子で切り取っています。 module_aが動的にインポートされると、その中にインポートされたモジュールと一緒にロードされます。
つまり依存グラフの新しいエントリポイントを作成しているだけです。
このファイルの依存グラフの一例です。
Vueコンポーネントをlazy loadする
Lazy loadとは何か、またそれが必要な理由はわかりました。 Vueアプリケーションでそれをどのように利用できるかを見てみましょう。
幸いこれは非常に簡単で、SFC全体とCSSおよびHTMLを同じ構文で遅延的に読み込むことができます。
const lazyComponent = () => import('Component.vue')
…必要なのはこれだけです!これで、コンポーネントは要求されたときにのみダウンロードされます。 Vueコンポーネントの動的ロードを呼び出すための最も一般的な方法は次のとおりです。
インポート付きの関数が呼び出されます。
const lazyComponent = () => import('Component.vue')
lazyComponent()
<template>
<div>
<lazy-component />
</div>
</template>
<script>
const lazyComponent = () => import('Component.vue')
export default {
components: { lazyComponent }
</script>
lazyComponent関数の呼び出しは、コンポーネントがテンプレートでレンダリングするように要求されたときにのみ行われることに注意してください。
例えば、このコード:
<lazy-component v-if = "false" />
DOMに追加されていないため、コンポーネントは動的にインポートされません(ただし、値がtrueに変わるとすぐに実行されます)。
サマリー
Lazy loadingはWebアプリケーションをより高性能にし、サイズを制限するための最良の方法の1つです。 Vueコンポーネントでlazy loadを使用する方法を学びました。このシリーズの次のパートでは、Vueアプリケーションコードをvue-routerルートとasyncルートで分割する方法を説明します。