名前空間方式 API からモジュラー API へのアップグレード

現在、バージョン 8 以前で compat ライブラリの名前空間方式 Firebase Web API を使用しているアプリは、このガイドの手順に沿ってモジュラー API に移行することを検討してください。

このガイドでは、名前空間方式 API に詳しく、アップグレードやモジュラー アプリの開発で webpackRollup などのモジュール バンドラを利用していることを前提としています。

開発環境でモジュール バンドラを使用することを強くおすすめします。使用しないと、モジュラー API の主なメリットであるアプリのサイズ削減を実現できません。SDK をインストールするには、npm または yarn が必要です。

このガイドのアップグレード手順は、Authentication SDK と Cloud Firestore SDK を使用する架空のウェブアプリをベースにしています。サンプルに取り組むことで、サポートされているすべての Firebase Web SDK のアップグレードに必要なコンセプトと実用的な手順を習得できます。

名前空間方式(compat)ライブラリについて

Firebase Web SDK には、次の 2 種類のライブラリが用意されています。

  • モジュラー - ウェブアプリをできる限り小さくして高速化するためのツリー シェイキング(未使用のコードの削除)を可能にする新しい API サーフェス。
  • 名前空間方式(compat - 以前のバージョンの SDK と完全な互換性がある、使い慣れた API サーフェス。すべての Firebase のコードを変更することなく、一度にアップグレードできます。名前空間方式と比較すると、互換ライブラリにはサイズやパフォーマンス上のメリットはほとんどありません。

このガイドでは、アップグレードを容易にするために互換ライブラリを利用することを前提としています。これらのライブラリを使用すると、モジュラー API 用にリファクタリングされたコードとともに、名前空間方式のコードを引き続き使用できます。つまり、アップグレード プロセスを進める際に、アプリのコンパイルとデバッグをより簡単に行うことができます。

Firebase Web SDK をほとんど使用していないアプリ(Authentication API の単純な呼び出しだけを行うアプリなど)では、互換ライブラリを使用せずに、以前の名前空間方式のコードをリファクタリングするほうが現実的です。このようなアプリをアップグレードする場合は、互換ライブラリを使用せずに、このガイドの「モジュラー API」の説明に従ってください。

アップグレード プロセスについて

アップグレード プロセスの各ステップは、アプリのソースの編集を完了してから、エラーなしでコンパイルして実行できるように、スコープが設定されています。アプリのアップグレードの大まかな流れは次のとおりです。

  1. モジュラー ライブラリと互換ライブラリをアプリに追加します。
  2. コードの import ステートメントを互換に更新します。
  3. 単一プロダクト(Authentication など)のコードをモジュラー スタイルにリファクタリングします。
  4. 省略可: 次に進む前に、Authentication のアプリサイズに関するメリットを実現するために、この時点で Authentication の互換ライブラリと Authentication の互換コードを削除します。
  5. 各プロダクト(Cloud Firestore、FCM など)の機能をモジュラー スタイルにリファクタリングし、すべての領域が完了するまでコンパイルとテストを行います。
  6. 初期化コードをモジュラー スタイルに更新します。
  7. 残りの互換ステートメントと互換コードをアプリからすべて削除します。

SDK の最新バージョンを入手する

はじめに、npm を使用してモジュラー ライブラリと互換ライブラリを入手します。

npm i firebase@10.4.0

# OR

yarn add firebase@10.4.0

import を互換に更新する

依存関係を更新した後もコードが引き続き動作するためには、import ステートメントを変更して、各インポートの「互換」バージョンを使用します。次に例を示します。

変更前: バージョン 8 以前

import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

変更後: 互換

// compat packages are API compatible with namespaced code
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';

モジュラー スタイルへのリファクタリング

名前空間方式 API はドットチェーンの名前空間とサービス パターンに基づいていますが、モジュラー アプローチでは、原則的に関数を中心にコードを記述します。モジュラー API の場合、firebase/app パッケージやその他のパッケージは、パッケージのすべてのメソッドを含む包括的なエクスポートを返しません。代わりに、パッケージの個々の関数がエクスポートされます。

モジュラー API では最初の引数としてサービスが渡され、関数がサービスの詳細を使用して残りの処理を行います。Authentication API と Cloud Firestore API の呼び出しをリファクタリングする 2 つの例で、その仕組みを見てみましょう。

例 1: Authentication の関数のリファクタリング

変更前: 互換

互換コードは名前空間方式のコードと同じですが、import は変更されています。

import firebase from "firebase/compat/app";
import "firebase/compat/auth";

const auth = firebase.auth();
auth.onAuthStateChanged(user => { 
  // Check for user status
});

変更後: モジュラー

getAuth 関数は最初のパラメータとして firebaseApp を受け取ります。名前空間方式 API とは異なり、onAuthStateChanged 関数は auth インスタンスからチェーンされません。これは独立した関数であり、最初のパラメータとして auth を受け取ります。

import { getAuth, onAuthStateChanged } from "firebase/auth";

const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => {
  // Check for user status
});

認証メソッド getRedirectResult の処理を更新する

モジュラー API では、getRedirectResult に対して互換性を破る変更が行われました。リダイレクト オペレーションが呼び出されない場合、モジュラー API では null ユーザーを含む UserCredential が返されましたが、名前空間方式 API では null が返されます。

変更前: 互換

const result = await auth.getRedirectResult()
if (result.user === null && result.credential === null) {
  return null;
}
return result;

変更後: モジュラー

const result = await getRedirectResult(auth);
// Provider of the access token could be Facebook, Github, etc.
if (result === null || provider.credentialFromResult(result) === null) {
  return null;
}
return result;

例 2: Cloud Firestore の関数のリファクタリング

変更前: 互換

import "firebase/compat/firestore"

const db = firebase.firestore();
db.collection("cities").where("capital", "==", true)
    .get()
    .then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    })
    .catch((error) => {
        console.log("Error getting documents: ", error);
    });

変更後: モジュラー

getFirestore 関数は最初のパラメータとして firebaseApp を受け取ります。これは、前の例の initializeApp から返されたものです。クエリを形成するコードは、モジュラー API では大きく異なります。チェーンはなく、querywhere などのメソッドが独立した関数として公開されるようになりました。

import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";

const db = getFirestore(firebaseApp);

const q = query(collection(db, "cities"), where("capital", "==", true));

const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
  // doc.data() is never undefined for query doc snapshots
  console.log(doc.id, " => ", doc.data());
});

Firestore DocumentSnapshot.exists への参照の更新

モジュール API では破壊的な変更が行われ、プロパティ firestore.DocumentSnapshot.exists がメソッドに変更されました。機能は基本的に同じ(ドキュメントが存在するかどうかのテスト)ですが、次に示すように新しいメソッドを使用するようにコードをリファクタリングする必要があります。

変更前: 互換

if (snapshot.exists) {
  console.log("the document exists");
}

変更後: モジュラー

if (snapshot.exists()) {
  console.log("the document exists");
}

例 3: 名前空間方式とモジュラーのコードスタイルの組み合わせ

アップグレード中に互換ライブラリを使用すると、モジュラー API 用にリファクタリングされたコードとともに、名前空間方式のコードも引き続き使用できます。つまり、Cloud Firestore 用の既存の名前空間方式のコードを維持したまま、Authentication や他の Firebase SDK のコードをモジュラー スタイルにリファクタリングし、両方のコードスタイルを混在させたままアプリを正常にコンパイルできます。Cloud Firestore などの 1 つのプロダクトの中でも、名前空間方式とモジュラーの API コードを混在させることができます。ただし、ここでは互換パッケージをインポートすることが条件となります。

import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import { getDoc } from 'firebase/firestore'

const docRef = firebase.firestore().doc();
getDoc(docRef);

なお、アプリが正常にコンパイルされたとしても、アプリから互換ステートメントと互換コードを完全に削除するまでは、モジュラー コードで得られるアプリサイズのメリットは得られないことに注意してください。

初期化コードの更新

モジュラー構文を使用するようにアプリの初期化コードを更新します。アプリ内のすべてのコードのリファクタリングを完了した後に、このコードの更新が重要です。これは、firebase.initializeApp() が互換 API とモジュラー API の両方のグローバル状態を初期化するのに対して、モジュラーの initializeApp() 関数はモジュラーの状態のみを初期化するためです。

変更前: 互換

import firebase from "firebase/compat/app"

firebase.initializeApp({ /* config */ });

変更後: モジュラー

import { initializeApp } from "firebase/app"

const firebaseApp = initializeApp({ /* config */ });

互換コードの削除

モジュラー API のサイズの利点を実現するには、最終的にすべての呼び出しを上記のモジュラー スタイルに変換し、すべての import "firebase/compat/* ステートメントをコードから削除する必要があります。これを行うと、firebase.* グローバル名前空間への参照と、名前空間方式 API スタイルのすべてのコードがなくなります。

window から互換ライブラリを使用する

モジュラー API は、ブラウザの window オブジェクトではなく、モジュールによって動作するように最適化されています。以前のバージョンのライブラリでは、window.firebase 名前空間を使用して Firebase の読み込みと管理を行うことが可能でした。この方法では未使用のコードを除去できないため、今後はおすすめしません。ただし、モジュラー ライブラリへのアップグレード パスをすぐに開始しない場合は、JavaScript SDK の互換バージョンを window で使用できます。

<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.4.0/firebase-auth-compat.js"></script>
<script>
   const firebaseApp = firebase.initializeApp({ /* Firebase config */ });
   const db = firebaseApp.firestore();
   const auth = firebaseApp.auth();
</script>

互換ライブラリは、内部ではモジュラー コードを使用していますが、名前空間方式 API と同じ API によって機能を提供しています。したがって、詳しい使用方法については、名前空間方式 API リファレンスと名前空間方式のコード スニペットをそのまま参照できます。この方法を長期間にわたって使用するのではなく、完全なモジュラー ライブラリへのアップグレードを開始することをおすすめします。

モジュラー SDK のメリットと制限事項

完全にモジュール化された SDK には、以前のバージョンと比べて次のようなメリットがあります。

  • モジュラー SDK を使用すると、アプリのサイズを大幅に小さくできます。最新の JavaScript モジュール形式を採用することで「ツリー シェイキング」の手法が可能になり、アプリで必要なアーティファクトのみをインポートできます。アプリによっては、モジュラー SDK でツリー シェイキングを行うと、名前空間方式 API を使用してビルドした同等のアプリよりも 80% も小さくなることがあります。
  • モジュラー SDK は、現行の機能開発のメリットを享受し続けますが、名前空間方式 API はそうではありません。