从版本 8 升级为模块化 Web SDK

对于目前正在使用 Firebase Web SDK 8 或更早版本的应用,应考虑按照本指南中的说明迁移到版本 9。

本指南假定您熟悉 v8,并且将利用模块打包器(例如 WebpackRollup)进行升级并持续进行 v9 的开发。

强烈建议在开发环境中使用模块打包器。如果您不使用模块打包器,则无法利用 v9 在缩减应用大小方面的主要优势。您需要使用 npmyarn 来安装 SDK。

本指南中的升级步骤将基于使用 Firebase Authentication 和 Cloud Firestore SDK 的虚构 Web 应用。通过学习这些示例,您可以掌握升级所有受支持的 Firebase Web SDK 所需了解的概念和实际步骤。

兼容型库简介

Firebase Web SDK v9 提供两种类型的库:

  • 模块化 - 一种新的 API 接口,用于实现摇树优化(移除未使用的代码),使您的 Web 应用尽可能精简快速。
  • 兼容型 - 一种熟悉的 API 接口,与 v8 SDK 完全兼容,让您无需一次性更改所有 Firebase 代码,即可升级到 v9。与 v8 对应的库相比,兼容型库在大小或性能方面的优势很少,甚至没有优势。

本指南假定您将利用 v9 兼容型库来推动升级。这些库让您可以一边继续使用 v8 对应的代码,一边逐步针对 v9 重构代码。这意味着,您在升级过程中可以更轻松地编译和调试应用。

对于极少调用 Firebase Web SDK 的应用(例如,只对 Authentication API 进行简单调用的应用)而言,可以直接重构 v8 的代码,而不需要使用 v9 兼容型库。如果要升级此类应用,请按照本指南中有关“v9 模块化库”的说明进行操作,而无需使用兼容型库。

升级流程简介

升级流程的每个步骤都限定了范围,这样您便可以完成应用源代码的修改,然后在不出现中断的情况下编译并运行源代码。总的来说,如需升级应用,您需要执行以下操作:

  1. 将 v9 库和兼容型库添加到您的应用中。
  2. 将代码中的 import 语句更新为使用 v9 兼容型库。
  3. 将针对单个产品(例如 Firebase Authentication)的代码重构为模块化样式。
  4. 可选:现在移除用于 Firebase Authentication 的兼容型库和兼容型代码,以便先在 Firebase Authentication 方面实现应用大小的精简,然后再继续。
  5. 将针对每个产品(例如 Cloud Firestore、FCM 等)的函数重构为模块化样式,然后进行编译和测试,直到完成所有方面。
  6. 将初始化代码更新为模块化样式。
  7. 移除应用中所有剩余的 v9 兼容型语句和代码。

获取 v9 SDK

使用 npm 获取 v9 库和兼容型库:

npm i firebase@9.22.0

# OR

yarn add firebase@9.22.0

将导入项更新为 v9 兼容型库

将依赖项从使用 v8 更新为使用 v9 Beta 版后,为了确保代码正常运行,请将 import 语句的内容更改为“兼容型”版本。例如:

之前:v8

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

之后:v9 兼容型库

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

重构为模块化样式

v8 对应的 API 基于点链命名空间和服务模式,而 v9 模块化方法意味着您的代码将主要围绕函数进行组织。在 v9 中,firebase/app 软件包和其他软件包不会返回包含软件包中所有方法的全面导出结果,而是会导出个别函数。

在 v9 中,服务作为第一个参数传递,函数随后会使用服务的详细信息执行其余操作。我们通过两个重构 Firebase Authentication 和 Cloud Firestore API 调用的示例来了解具体的工作原理。

示例 1:重构 Firebase Authentication 函数

之前:v9 兼容型库

v9 兼容型库的代码与 v8 的代码完全相同,但导入方面发生了变化。

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

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

之后:v9 模块化库

getAuth 函数将 firebaseApp 作为其第一个参数。 onAuthStateChanged 函数不会像在 v8 中那样需要从 auth 实例串联;而是会成为一个自由函数,将 auth 作为其第一个参数。

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

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

更新对 Auth 方法 getRedirectResult 的处理方式

v9 针对 getRedirectResult 引入了一项破坏性更改;即如果没有调用重定向操作,v9 会直接返回 null,先前的 v8 则与之不同,它会返回包含 null 用户的 UserCredential

之前:v9 兼容型库

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

之后:v9 模块化库

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 函数

之前:v9 兼容型库

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);
    });

之后:v9 模块化库

getFirestore 函数将 firebaseApp 作为其第一个参数,后者是从前面的示例中的 initializeApp 返回的。请注意,在 v9 中,代码构建查询的方式有很大不同;没有串联,并且 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 的引用

版本 9 引入了一项破坏性更改,即属性 firestore.DocumentSnapshot.exists 已更改为方法。其功能基本相同(测试文档是否存在),但您必须重构代码以使用 v9 方法,如下所示:

之前:v9 兼容型库

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

之后:v9 模块化库

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

示例 3:组合使用 v8 和 v9 代码样式

在升级过程中使用兼容型库让您可以一边继续使用 v8 对应的代码,一边逐步针对 v9 重构代码。这意味着,您在将 Firebase Authentication 代码或其他 Firebase SDK 代码重构为 v9 样式的过程中,可以保留 Cloud Firestore 的现有 v8 代码,并且仍然能使用两种代码样式成功编译应用。产品(如 Cloud Firestore)中的 v8 和 v9 代码也是如此;新旧代码样式可以共存,前提是您导入的是兼容型软件包:

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

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

请注意,虽然您的应用将可正常编译,但只有从应用中完全移除兼容型语句和代码,才能获享模块化代码在应用大小方面的优势。

更新初始化代码

更新应用的初始化代码,使用新的模块化 v9 语法。重构应用中的所有代码后,请务必更新初始化代码;这是因为 firebase.initializeApp() 会初始化兼容型和模块化 API 的全局状态,而模块化 initializeApp() 函数仅会初始化模块化 API 的状态。

之前:v9 兼容型库

import firebase from "firebase/compat/app"

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

之后:v9 模块化库

import { initializeApp } from "firebase/app"

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

移除兼容型代码

为了使用 v9 模块化 SDK 精简应用大小,您最终应将所有调用转换为上述的模块化样式,并从代码中移除所有 import "firebase/compat/* 语句。完成后,代码中不应再包含对 firebase.* 全局命名空间的引用或任何其他 v8 SDK 样式代码的引用。

从窗口中使用兼容型库

v9 SDK 针对使用模块(而非浏览器的 window 对象)运行进行了优化。该库之前的版本允许使用 window.firebase 命名空间加载和管理 Firebase。建议您以后不要采用这种做法,因为这种方法不允许消除未使用的代码。不过,对于不希望立即开始模块升级的开发者,JavaScript SDK 的兼容性版本也支持 window

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

兼容性库在后台使用模块化 v9 代码,并为其提供与 v8 SDK 相同的 API;这意味着您可以参考 v8 API 参考文档和 v8 代码了解详情。此方法不建议长期使用,而是应作为着手点来升级到完全模块化 v9 库。

v9 的优势和局限

与早期版本相比,完全模块化的 v9 具有以下优势:

  • v9 可实现应用大小的大幅缩减。 它采用新型的 JavaScript 模块格式,允许执行“摇树优化”做法 - 这样就只需导入应用需要的工件。与使用 v8 构建的类似应用相比,使用 v9 进行摇树优化可使大小(以 Kb 为单位)减少 80%,具体取决于您的应用。
  • v9 将继续受益于后续的功能开发,而 v8 将在未来的某个时间停用。