この Codelab では、未使用の不要な依存関係を削除して、次のアプリケーションのパフォーマンスを改善します。

測定
最適化を行う前に、まずウェブサイトのパフォーマンスを測定することをおすすめします。
- サイトをプレビューするには、[View App] を押します。次に、[Fullscreen]
を押します。
お気に入りの子猫をクリックしてみましょう。このアプリケーションでは Firebase の Realtime Database が使用されているため、スコアがリアルタイムで更新され、アプリケーションを使用している他のすべてのユーザーと同期されます。🐈
- 「Ctrl+Shift+J」(Mac の場合は「Command+Option+J」)を押して DevTools を開きます。
- [ネットワーク] タブをクリックします。
- [Disable cache] チェックボックスをオンにします。
- アプリを再読み込みします。

このシンプルなアプリケーションを読み込むために、1 MB 近くの JavaScript が送信されています。
DevTools でプロジェクトの警告を確認します。
- [Console] タブをクリックします。
Filter入力の横にあるレベルのプルダウンでWarningsが有効になっていることを確認します。

- 表示された警告を確認します。

このアプリケーションで使用されているライブラリの 1 つである Firebase は、パッケージ全体をインポートするのではなく、使用するコンポーネントのみをインポートするようにデベロッパーに警告しています。つまり、このアプリケーションでは、未使用のライブラリを削除して読み込みを高速化できます。
特定のライブラリが使用されているものの、よりシンプルな代替手段がある場合もあります。不要なライブラリを削除するというコンセプトについては、このチュートリアルの後半で説明します。
バンドルを分析しています
このアプリケーションには、主に 2 つの依存関係があります。
- Firebase: iOS、Android、ウェブ アプリケーション向けの便利なサービスを多数提供するプラットフォーム。ここでは、Realtime Database を使用して、各子猫の情報をリアルタイムで保存、同期しています。
- Moment.js: JavaScript で日付を簡単に処理できるようにするユーティリティ ライブラリ。各子猫の生年月日は Firebase データベースに保存され、
momentを使用して週単位で年齢が計算されます。
わずか 2 つの依存関係が、1 MB 近くのバンドルサイズにどのように影響するのでしょうか。その理由の一つは、依存関係にはそれぞれ独自の依存関係があるため、依存関係「ツリー」のすべての深さ / ブランチを考慮すると、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()
]
};
アプリケーションが再読み込みされると、アプリ自体ではなく、バンドル全体の可視化が表示されます。

子猫 🐱 を見るほどかわいくはありませんが、非常に役立ちます。 いずれかのパッケージにカーソルを合わせると、そのサイズが次の 3 つの方法で表示されます。
| 統計情報のサイズ | 縮小または圧縮前のサイズ。 |
|---|---|
| 解析サイズ | コンパイル後のバンドル内の実際のパッケージのサイズ。 webpack のバージョン 4(このアプリケーションで使用)は、コンパイルされたファイルを自動的に縮小します。そのため、これは統計サイズよりも小さくなります。 |
| gzip 圧縮後のサイズ | gzip エンコードで圧縮された後のパッケージのサイズ。このトピックについては、別のガイドで説明しています。 |
webpack-bundle-analyzer ツールを使用すると、バンドルの大部分を占める未使用または不要なパッケージを簡単に特定できます。
未使用のパッケージを削除する
このビジュアリゼーションは、firebase パッケージがデータベースだけでなく、多くの要素で構成されていることを示しています。これには、次のような追加パッケージが含まれます。
firestoreauthstoragemessagingfunctions
これらはすべて 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 のみが使用されていました。firebase/app のインポートは、各サービスの API サーフェスを設定するもので、常に必要です。
lodash などの他の多くの一般的なライブラリでも、デベロッパーはパッケージのさまざまな部分を選択的にインポートできます。アプリケーションのライブラリ インポートを更新して、使用されているもののみを含めるようにするだけで、パフォーマンスが大幅に向上する可能性があります。
バンドルサイズはかなり小さくなりましたが、まだやるべきことがあります。😈
不要なパッケージの削除
Firebase とは異なり、moment ライブラリの一部をインポートするのは簡単ではありませんが、完全に削除することはできるかもしれません。
各子猫の誕生日は、Firebase データベースに Unix 形式(ミリ秒)で保存されます。

これは、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 などのライブラリを使用すると、この処理を大幅に簡素化できます。
すべてはトレードオフであり、サードパーティ ライブラリに依存するのではなく、カスタム ソリューションをロールアウトする複雑さと労力に見合う価値があるかどうかを判断することが重要です。