用新的Firebase v9.x Web SDK重构一个React应用

语言: CN / TW / HK

Firebase Web SDK第9版的发布,在管理用户和查询数据库的方法上有了突破性的变化。在Firebase v8.x中编写的代码在v9.x中使用时将会出现错误,这就需要进行重构。

在这篇文章中,我们将学习如何重构一个使用Firebase Web SDK v8.x到v9.x的React应用程序,这也被称为模块化的Web SDK。对于我们的例子,我们将使用一个用v8.x构建的亚马逊克隆,并将其重构到v9.x。

前提条件

要跟上这个教程,你应该熟悉React和Firebase v8.x,你还应该在你的机器上安装Node.js。

介绍Firebase v9.x Web SDK

新的Web SDK摆脱了第8版中使用的命名空间方法。取而代之的是,它采用了一种优化的模块化格式,以消除不使用的代码,例如,树形摇动,从而大大减少了JavaScript捆绑的大小。

向模块化方法的过渡引入了一些破坏性的变化,使得新的库向后不兼容,并导致v8.x中使用的代码在新的Firebase v9.x SDK中出现错误。

下面的代码显示了新库中引入的一些破坏性变化。

``` // VERSION 8 import firebase from 'firebase/app'; import 'firebase/auth';

firebase.initializeApp(); const auth = firebase.auth();

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

// VERSION 9 EQUIVALENT import { initializeApp } from 'firebase/app'; import { getAuth, onAuthStateChanged } from 'firebase/auth';

const firebaseApp = initializeApp(); const auth = getAuth(firebaseApp);

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

```

上面的两个代码样本都监控了一个用户状态。虽然两者使用的代码行数相似,但在v9.x中,我们没有导入firebase 命名空间或firebase/auth 副作用,该副作用将认证服务增强到firebase 命名空间中,而是导入并使用单个函数。

这些变化利用了现代JavaScript工具如Webpack和Rollup的代码消除功能。

例如,上面的v8.x代码包括以下代码片断。

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

```

auth 是一个命名空间和一个服务,包含onAuthStateChanged 方法。该命名空间还包含像signInWithEmailAndPasswordcreateUserWithEmailAndPasswordsignOut ,这些方法没有被代码使用。当我们捆绑整个代码时,这些未使用的方法也会被包含在捆绑中,从而导致相对大小的增加。

尽管像Webpack和Rollup这样的捆绑器可以用来消除未使用的代码,但由于命名空间的方法,它们将没有任何效果。解决这个问题是重塑API表面的主要目标之一,以采取模块化的形式。要了解更多关于新库变化背后的原因,请查看Firebase官方博客

Firebase兼容性库

新的SDK还包括一个具有熟悉的API表面的兼容库,它与v8.x完全兼容。兼容库允许我们在同一个代码库中同时使用新旧API,使我们能够逐步重构我们的应用而不破坏它。我们可以通过对导入路径做一些调整来使用兼容库,如下所示。

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

```

当我们重构我们的Amazon克隆应用时,我们将利用这个库的优势。

Firebase Web SDK v9.x的好处

简而言之,Firebase Web SDK v9.x提供了缩小的尺寸和增加的整体性能。通过像Webpack和Rollup这样的JavaScript工具来利用代码消除功能,新的Web SDK提供了更快的网络体验。据Firebase官方Twitter账户称,由于采用了新的模块化形状,新的SDK据说比前代产品小了约80%。

Firebase Tweet v9 80 Percent Size

设置我们的React应用进行重构

现在我们已经熟悉了新的SDK,让我们来学习如何重构我们的v8.x应用。本节中我们将使用的亚马逊克隆应用是一个使用Firebase和Strapi构建的电子商务应用。

在我们的应用中,我们使用Firebase来增加一些功能,比如用Firebase认证来管理用户身份,用Cloud Firestore来存储认证用户购买的产品。我们使用Strapi来处理在应用程序上购买的产品的付款。最后,我们用Express.js创建了一个API,用Strapi客户端的秘密来响应即将用Firebase云功能购买产品的客户。

你可以访问该网站的部署版本,它看起来像下面的图片。

Amazon Clone App

请随意玩玩这个应用,以便更好地理解我们在这篇文章中的工作内容。

设置亚马逊的克隆应用

在我们开始编码之前,首先,让我们从GitHub上克隆repo并安装必要的npm包。打开你的终端,导航到你想存储React应用程序的文件夹。添加以下命令。

``` $ git clone http://github.com/Tammibriggs/Amazon-clone-FirebaseV8.git $ cd Amazon-clone-FirebaseV8

```

现在我们已经成功克隆了 repo,在安装包之前,我们需要将package.json 文件中的Firebase版本改为v9.x。

在根目录下,打开package.json 文件,用"firebase": "9.2.0" 替换依赖对象中的"firebase": "8.10.0" 。现在,让我们通过在终端运行以下命令来安装我们应用程序的依赖项。

``` $ npm install $ cd functions $ npm install

```

尽管我们已经设置并安装了我们应用程序的所有依赖项,但如果我们尝试用npm start 来运行应用程序,它就会出现错误。为了避免这种情况,我们需要修复我们的应用程序的破坏性变化,这一点我们很快就会做到。

React应用程序的结构

我们的应用程序的src 目录的结构如下,但我们已经删除了所有的样式文件,使其看起来更短。

``` src ┣ Checkout ┃ ┣ Checkout.js ┃ ┣ CheckoutProduct.js ┃ ┗ Subtotal.js ┣ Header ┃ ┗ Header.js ┣ Home ┃ ┣ Home.js ┃ ┗ Product.js ┣ Login ┃ ┗ Login.js ┣ Orders ┃ ┣ Order.js ┃ ┗ Orders.js ┣ Payment ┃ ┣ axios.js ┃ ┗ Payment.js ┣ App.js ┣ firebase.js ┣ index.js ┣ reducer.js ┣ reportWebVitals.js ┗ StateProvider.js

```

我们将只处理使用Firebase服务的文件,firebaseApp.jsHeader.jsLogin.jsPayment.jsOrders.js

将亚马逊的克隆文件重构为一个模块化的方法

让我们更新到v9.x的compat库,帮助我们逐步迁移到模块化方法,直到我们不再需要compat库。

升级过程遵循一个重复的模式;首先,它将单一服务(如身份验证)的代码重构为模块化风格,然后删除该服务的compat库。

更新导入到9.x版本的compat库

前往src 目录中的firebase.js 文件,修改v8.x的导入,使其看起来像以下代码。

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

```

仅仅做了一些改动,我们就将应用程序更新到了v9.x compat。现在,我们可以用npm start 来启动我们的应用程序,它不会出现任何错误。我们还应该在本地启动Firebase函数,以公开从Strapi获取客户秘密的API。

在你的终端,改变到functions 目录,运行以下命令来启动该函数。

``` $ firebase emulators:start

```

重构认证代码

Login.js,App.js, 和Header.js, 我们使用了Firebase认证服务。首先,让我们重构Login.js 文件中的代码,在这里我们创建了创建用户的功能,并通过FirebasecreateUserWithEmailAndPasswordsignInWithEmailAndPassword 方法来签署他们。当我们扫描Login.js 文件的时候,我们会看到下面的v8.x代码。

``` // src/Login/Login.js const signIn = e => { ... // signIn an existing user with email and password auth .signInWithEmailAndPassword(email, password) .... }

const regiter = e => { ... // Create a new user with email and password using firebase auth .createUserWithEmailAndPassword(email, password) .... }

```

为了遵循模块化的方法,我们将从auth 模块中导入signInWithEmailAndPasswordcreateUserWithEmailAndPassword 方法,然后更新代码。重构后的版本将看起来像下面的代码。

``` // src/Login/Login.js import {signInWithEmailAndPassword, createUserWithEmailAndPassword} from 'firebase/auth'

... const signIn = e => { ... // signIn an existing user with email and password signInWithEmailAndPassword(auth, email, password) ... } const regiter = e => { ... // Create a new user with email and password using firebase createUserWithEmailAndPassword(auth, email, password) ... }

```

现在,让我们重构一下App.jsHeader.js 文件。在App.js 文件中,我们使用onAuthStateChanged 方法来监控用户登录状态的变化。

``` // src/App.js useEffect(() => { auth.onAuthStateChanged(authUser => { ... }) }, [])

```

上述代码的模块化v9.x看起来像下面这段。

``` // src/App.js import {onAuthStateChanged} from 'firebase/auth'

... useEffect(() => { onAuthStateChanged(auth, authUser => { ... }) }, [])

```

Header.js 文件中,我们使用signOut 方法来签出已认证的用户。

``` // src/Header/Header.js const handleAuthentication = () => { ... auth.signOut() ... }

```

更新上面的代码,使其看起来像下面的代码段。

``` // src/Header/Header.js import {signOut} from 'firebase/auth' ... const handleAuthentication = () => { ... signOut(auth) ... }

```

现在我们已经完成了对所有认证代码的重构,现在是时候删除compat库以获得我们的规模优势了。在firebase.js 文件中,用以下代码替换import 'firebase/compat/auth'const auth = firebaseApp.auth()

``` import {getAuth} from 'firebase/auth' ... const auth = getAuth(firebaseApp)

```

重构Cloud Firestore代码

重构Cloud Firestore代码的过程与我们刚才对认证代码所做的相似。我们将与Payment.jsOrders.js 文件一起工作。在Payment.js ,我们使用Firestore来存储在网站上支付产品的用户的数据。在Payment.js ,我们会发现下面的v8.x代码。

``` // src/Payment/Payment.js ... db .collection('users') .doc(user?.uid) .collection('orders') .doc(paymentIntent.id) .set({ basket: basket, amount: paymentIntent.amount, created: paymentIntent.created }) ...

```

为了重构代码,我们首先要导入必要的函数,然后更新代码的其他部分。上面代码的v9.x看起来像下面这样。

``` // src/Payment/Payment.js import {doc, setDoc} from 'firebase/firestore'

... const ref = doc(db, 'users', user?.uid, 'orders', paymentIntent.id) setDoc(ref, { basket: basket, amount: paymentIntent.amount, created: paymentIntent.created }) ...

```

Orders.js 文件中,我们使用onSnapshot 方法来获得Firestore中的数据的实时更新。v9.x的代码看起来如下。

``` // src/Orders/Orders.js .... db .collection('users') .doc(user?.uid) .collection('orders') .orderBy('created', 'desc') .onSnapshot(snapshot => { setOrders(snapshot.docs.map(doc => ({ id: doc.id, data: doc.data() }))) }) ...

```

v9.x的对应代码如下。

``` import {query, collection, onSnapshot, orderBy} from 'firebase/firestore'

... const orderedOrders = query(ref, orderBy('created', 'desc')) onSnapshot(orderedOrders, snapshot => { setOrders(snapshot.docs.map(doc => ({ id: doc.id, data: doc.data() }))) }) ...

```

现在我们已经完成了对所有Cloud Firestore代码的重构,让我们删除compat库。在firebase.js 文件中,用以下代码替换import 'firebase/compat/firestore'const db = firebaseApp.firestore()

``` import { getFirestore } from "firebase/firestore"; ... const db = getFirestore(firebaseApp) ...

```

更新初始化代码

将我们的亚马逊克隆应用升级到新的模块化v9.x语法的最后一步是更新初始化代码。在firebase.js 文件中,用以下函数替换import firebase from 'firebase/compat/app'; 和 constfirebaseApp = firebase.initializeApp(firebaseConfig)

``` import { initializeApp } from "firebase/app" ... const firebaseApp = initializeApp(firebaseConfig) ...

```

现在,我们已经成功地升级了我们的应用程序,以遵循新的v9.x模块化格式。

结论

新的Firebase v9.x SDK由于采用了模块化格式,所以比它的前辈v8.x提供了更快的网络体验。本教程介绍了新的SDK,并解释了如何使用其紧凑的库来反映一个React应用。你应该能够按照本文介绍的方法和步骤,将你自己的应用程序升级到最新版本。

如果你在重构React应用程序时仍有困难,请务必查看以下Firebase支持社区。

The postRefactor a React app with the new Firebase v9.x Web SDKappeared first onLogRocket Blog.