使用していないコードを削除する

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

アプリのスクリーンショット

測定

最適化を追加する前に、ウェブサイトのパフォーマンスを測定することをおすすめします。

  • サイトをプレビューするには、[アプリを表示] を押してから、[全画面表示] 全画面表示 を押します。

ぜひお気に入りの子猫をクリックしてください。このアプリでは Firebase の Realtime Database が使用されているため、スコアはリアルタイムで更新され、アプリを使用している他のすべてのユーザーと同期されます。🐈

  1. Ctrl+Shift+J(Mac の場合は Command+Option+J)キーを押して DevTools を開きます。
  2. [ネットワーク] タブをクリックします。
  3. [キャッシュを無効にする] チェックボックスをオンにします。
  4. アプリを再読み込みします。

元のバンドルサイズが 992 KB

このシンプルなアプリケーションを読み込むために、1 MB 近くの JavaScript が送信されています。

DevTools でプロジェクトの警告を確認します。

  • [コンソール] タブをクリックします。
  • 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()
  ]
};

アプリが再読み込みされると、アプリ自体ではなく、バンドル全体の可視化が表示されます。

Webpack Bundle Analyzer

子猫 🐱? を見るほどかわいいものではありませんが、非常に役立ちます。 パッケージにカーソルを合わせると、サイズが 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] パネルを開くと、バンドルのサイズが大幅に削減されていることがわかります。

バンドルサイズを 480 KB に縮小

バンドルのサイズの半分以上が削除されました。Firebase にはさまざまなサービスが用意されており、デベロッパーは実際に必要なサービスのみを組み込むことができます。このアプリでは、すべてのデータの保存と同期に firebase/database のみが使用されています。各サービスの API サーフェスを設定する firebase/app インポートは常に必要です。

lodash などの他の一般的なライブラリでも、パッケージのさまざまな部分を選択的にインポートできます。アプリケーションのライブラリのインポートを更新して、使用されているものだけを含めるようにすることで、大きなパフォーマンス向上を実現できます。

バンドルサイズはかなり削減されましたが、まだやるべきことはあります。😈

不要なパッケージの削除

Firebase とは異なり、moment ライブラリの一部をインポートすることは簡単ではありませんが、完全に削除することはできますか?

各かわいい子猫の誕生日は、Unix 形式(ミリ秒)で 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>
    `})
});

アプリを再読み込みして、[ネットワーク] パネルをもう一度確認します。

バンドルのサイズを 225 KB に縮小

バンドルのサイズはさらに半分以上削減されました。

まとめ

この Codelab では、特定のバンドルを分析する方法と、未使用または不要なパッケージを削除することが非常に有用な理由を理解できるはずです。この手法でアプリの最適化を開始する前に、大規模なアプリではこの手法が大幅に複雑になる可能性があることを理解しておくことが重要です

未使用のライブラリを削除する場合は、バンドルのどの部分が使用されていて、どの部分が使用されていないかを特定します。どこでも使用されていないように見える謎めいたパッケージについては、一歩下がって、どのトップレベルの依存関係がそれを必要とする可能性があるかを確認します。可能であれば、それらを相互に切り離す方法を見つけてください。

不要なライブラリを削除する場合、状況は少し複雑になります。チームと緊密に連携して、コードベースの一部を簡素化できるかどうかを確認することが重要です。このアプリで moment を削除することは、毎回正しいことのように思えますが、タイムゾーンや異なる言語 / 地域を処理する必要がある場合はどうすればよいでしょうか。より複雑な日付操作が必要な場合はどうすればよいでしょうか。日時を操作したり解析したりするのは非常に難しい作業ですが、momentdate-fns などのライブラリを使用すると、この作業を大幅に簡素化できます。

すべてはトレードオフであり、サードパーティ ライブラリに依存するのではなく、カスタム ソリューションを導入する複雑さと労力に見合うかどうかを判断することが重要です。