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

この Codelab では、次のアプリのパフォーマンスを向上させるために、使用されていない不要な依存関係を削除します。

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

測定

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

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

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

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

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

このシンプルなアプリケーションを読み込むため、約 1 MB 相当の JavaScript が提供されています。

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

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

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

Webpack バンドル アナライザ

子猫を見るほどかわいいわけではありませんが、それでも信じられないほど助けになります。パッケージにカーソルを合わせると、サイズが次の 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 ライブラリの一部をインポートするのは簡単ではありませんが、完全に削除することは可能ですか?

かわいい子猫の誕生日は、Firebase データベースに Unix 形式(ミリ秒)で保存されます。

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] パネルを確認します。

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

セットのサイズがまた半分以下に減りました。

まとめ

この Codelab では、特定のバンドルを分析する方法と、使用されていないパッケージや不要なパッケージを削除することがなぜ有用なのかについて、十分に理解している必要があります。この手法でアプリケーションの最適化を開始する前に、大規模なアプリケーションでは、この方法は非常に複雑になる可能性があることを知っておいてください

未使用のライブラリの削除に関しては、バンドルのどの部分が使用されているかと、どの部分が使用されないかを確認してください。どこからも使用されていないように見えるミステリアスなパッケージの場合は、一歩引いて、どのトップレベルの依存関係にそれが必要かを確認してください。それらを互いに分離できる方法を探してください。

不要なライブラリを削除する場合、事態は少し複雑になる可能性があります。チームと緊密に連携して、コードベースの一部を簡素化できる可能性があるかどうかを確認することが重要です。このアプリケーションで moment を削除するのが毎回正しいように思えるかもしれませんが、処理しなければならないタイムゾーンやロケールがある場合はどうでしょうか。または、より複雑な日付操作がある場合はどうすればよいでしょうか。日時の操作および解析では、非常に複雑な処理が行われることがありますが、momentdate-fns などのライブラリを使用すると、この処理が大幅に簡素化されます。

すべてはトレードオフの関係であり、サードパーティのライブラリに依存せずにカスタム ソリューションを展開するための複雑さと労力に見合った価値があるかどうかを見極めることが重要です。