この Codelab では、未使用の依存関係と不要な依存関係を削除して、次のアプリのパフォーマンスを改善します。
測定
最適化を追加する前に、ウェブサイトのパフォーマンスを測定することをおすすめします。
- サイトをプレビューするには、[View App] を押してから、[Fullscreen] を押します。
ぜひお気に入りの子猫をクリックしてください。このアプリケーションでは Firebase の Realtime Database が使用されているため、スコアはリアルタイムで更新され、アプリケーションを使用する他のすべてのユーザーと同期されます。🐈
- Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
- [ネットワーク] タブをクリックします。
- [キャッシュを無効にする] チェックボックスをオンにします。
- アプリを再読み込みします。
このシンプルなアプリケーションを読み込むために、ほぼ 1 MB 分の JavaScript が送信されています。
DevTools でプロジェクトの警告を確認します。
- [コンソール] タブをクリックします。
Filter
入力の横にあるレベル プルダウンで、Warnings
が有効になっていることを確認します。
- 表示された警告を確認します。
このアプリで使用されているライブラリの 1 つである Firebase は、パッケージ全体ではなく、使用しているコンポーネントのみをインポートするようデベロッパーに警告することで、善意の行為を行っています。つまり、このアプリケーションには、読み込みを高速化するために削除できる未使用のライブラリがあります。
特定のライブラリが使用されているにもかかわらず、より簡単な代替手段がある場合もあります。不要なライブラリの削除のコンセプトについては、このチュートリアルの後半で説明します。
バンドルの分析
このアプリケーションには、主に次の 2 つの依存関係があります。
- Firebase: iOS、Android、ウェブ アプリケーションにさまざまな便利なサービスを提供するプラットフォーム。この場合、Realtime Database を使用して、各子猫の情報をリアルタイムで保存および同期します。
- Moment.js: JavaScript で日付を簡単に処理できるようにするユーティリティ ライブラリ。各子猫の誕生日は Firebase データベースに保存され、
moment
を使用して週単位の年齢が計算されます。
2 つの依存関係だけでバンドルのサイズが 1 MB 近くになるというのは、どういうことでしょうか?理由の 1 つは、すべての依存関係が独自の依存関係を持つことができるためです。依存関係「ツリー」のすべての深さ/ブランチを考慮すると、2 つよりもはるかに多くの依存関係が存在する可能性があります。多くの依存関係が含まれていると、アプリケーションが比較的短時間で大きくなります。
バンドルを分析して、何が起こっているかを把握します。コミュニティが作成したさまざまなツール(webpack-bundle-analyzer
など)を使用して、この作業を行うことができます。
このツールのパッケージは、devDependency
としてアプリにすでに含まれています。
"devDependencies": {
//...
"webpack-bundle-analyzer": "^2.13.1"
},
つまり、webpack 構成ファイルで直接使用できます。webpack.config.js
の一番最初にインポートします。
const path = require("path");
//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
次に、ファイルを最後までスクロールして、plugins
配列内にプラグインとして追加します。
module.exports = {
//...
plugins: [
//...
new BundleAnalyzerPlugin()
]
};
アプリケーションが再読み込みされると、アプリ自体ではなくバンドル全体が可視化されます。
子どもたちの子猫 MONTH を見るとかわいいとはいえ、とても助けになります。 いずれかのパッケージにカーソルを合わせると、そのサイズが 3 つの異なる方法で表示されます。
統計情報のサイズ | 圧縮や圧縮前のサイズ。 |
---|---|
解析サイズ | コンパイル後のバンドル内の実際のパッケージのサイズ。 このアプリケーションで使用されている webpack のバージョン 4 では、コンパイル済みのファイルが自動的に圧縮されるため、統計情報のサイズよりも小さくなります。 |
GZIP 圧縮後のサイズ | gzip エンコードで圧縮された後のパッケージのサイズ。このトピックについては、別のガイドをご覧ください。 |
webpack-bundle-analyzer ツールを使用すると、バンドルの大部分を占める未使用または不要なパッケージを簡単に特定できます。
未使用のパッケージの削除
このビジュアリゼーションは、firebase
パッケージがデータベースだけでなく多くの要素で構成されていることを示しています。これには、次のような追加パッケージが含まれています。
firestore
auth
storage
messaging
functions
これらはすべて Firebase が提供する優れたサービスですが(詳しくはドキュメントをご覧ください)、いずれもアプリで使用されていないため、すべてインポートする必要はありません。
webpack.config.js
の変更を元に戻して、アプリケーションを再度表示します。
- プラグインのリストから
BundleAnalyzerPlugin
を削除します。
plugins: [
//...
new BundleAnalyzerPlugin()
];
- 次に、未使用のインポートをファイルの先頭から削除します。
const path = require("path");
//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
アプリケーションが正常に読み込まれるはずです。src/index.js
を変更して、Firebase のインポートを更新します。
import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';
これで、アプリの再読み込み時に DevTools の警告が表示されなくなります。DevTools の [Network] パネルを開くと、バンドルサイズが大幅に削減されます。
バンドルのサイズの半分以上が削除されました。Firebase にはさまざまなサービスが用意されており、デベロッパーは実際に必要なサービスのみを組み込むことができます。このアプリでは、すべてのデータの保存と同期に firebase/database
のみが使用されています。各サービスの API サーフェスを設定する firebase/app
インポートは常に必要です。
lodash
などの他の一般的なライブラリでも、パッケージのさまざまな部分を選択的にインポートできます。アプリケーションのライブラリのインポートを更新して、使用されているものだけを含めるようにすることで、大きなパフォーマンス向上を実現できます。
バンドルサイズはかなり削減されましたが、まだやるべきことはあります。😈
不要なパッケージの削除
Firebase とは異なり、moment
ライブラリの一部をインポートすることは簡単ではありませんが、完全に削除することはできますか?
各かわいい子猫の誕生日は、Unix 形式(ミリ秒)で Firebase データベースに保存されます。
これは、1970 年 1 月 1 日 00:00 UTC からの経過時間(ミリ秒数)で表される特定の日時のタイムスタンプです。現在の日時を同じ形式で計算できる場合は、各子猫の年齢を週単位で計算する小さな関数を作成できます。
いつものように、手順に沿ってコピー&ペーストしないようにしてください。まず、src/index.js
のインポートから moment
を削除します。
import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';
データベースの値の変更を処理する Firebase イベント リスナーが存在します。
favoritesRef.on("value", (snapshot) => { ... })
その上に、指定した日付からの週数を計算する小さな関数を追加します。
const ageInWeeks = birthDate => {
const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
const diff = Math.abs((new Date).getTime() - birthDate);
return Math.floor(diff / WEEK_IN_MILLISECONDS);
}
この関数では、現在の日時 (new Date).getTime()
と生年月日(ミリ秒単位の birthDate
引数)のミリ秒の差が計算され、1 週間のミリ秒数で除算されます。
最後に、代わりにこの関数を利用して、イベント リスナーから moment
のすべてのインスタンスを削除できます。
favoritesRef.on("value", (snapshot) => { const { kitties, favorites, names, birthDates } = snapshot.val(); favoritesScores = favorites; kittiesList.innerHTML = kitties.map((kittiePic, index) => {const birthday = moment(birthDates[index]);return ` <li> <img src=${kittiePic} onclick="favKittie(${index})"> <div class="extra"> <div class="details"> <p class="name">${names[index]}</p><p class="age">${moment().diff(birthday, 'weeks')} weeks old</p><p class="age">${ageInWeeks(birthDates[index])} weeks old</p> </div> <p class="score">${favorites[index]} ❤</p> </div> </li> `}) });
アプリケーションを再読み込みして、[Network] パネルをもう一度確認します。
バンドルのサイズはさらに半分以上削減されました。
まとめ
この Codelab では、特定のバンドルを分析する方法と、未使用または不要なパッケージを削除することが非常に有用な理由を理解できます。この手法でアプリの最適化を開始する前に、大規模なアプリではこの手法が大幅に複雑になる可能性があることを理解しておくことが重要です。
未使用のライブラリを削除する場合は、バンドルのどの部分が使用されていて、どの部分が使用されていないかを特定します。どこでも使用されていないように見える謎めいたパッケージについては、一歩下がって、どのトップレベルの依存関係がそれを必要とする可能性があるかを確認します。可能であれば、それらを相互に切り離す方法を見つけてください。
不要なライブラリを削除する場合、状況は少し複雑になります。チームと緊密に連携して、コードベースの一部を簡素化する余地があるかどうか確認することが重要です。このアプリで moment
を削除することは、毎回正しいことのように思えますが、タイムゾーンや異なる言語 / 地域を処理する必要がある場合はどうすればよいでしょうか。より複雑な日付操作が必要な場合はどうすればよいでしょうか。日付/時刻の操作や解析は処理が複雑になる可能性があり、moment
や date-fns
などのライブラリを使用すると大幅に簡素化されます。
すべてはトレードオフであり、サードパーティ ライブラリに依存するのではなく、カスタム ソリューションを導入する複雑さと労力に見合う価値があるかどうかを判断することが重要です。