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

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 https://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.

Guess you like

Origin juejin.im/post/7068177568133971982