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
方法。该命名空间还包含像signInWithEmailAndPassword
、createUserWithEmailAndPassword
、signOut
,这些方法没有被代码使用。当我们捆绑整个代码时,这些未使用的方法也会被包含在捆绑中,从而导致相对大小的增加。
尽管像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%。
设置我们的React应用进行重构
现在我们已经熟悉了新的SDK,让我们来学习如何重构我们的v8.x应用。本节中我们将使用的亚马逊克隆应用是一个使用Firebase和Strapi构建的电子商务应用。
在我们的应用中,我们使用Firebase来增加一些功能,比如用Firebase认证来管理用户身份,用Cloud Firestore来存储认证用户购买的产品。我们使用Strapi来处理在应用程序上购买的产品的付款。最后,我们用Express.js创建了一个API,用Strapi客户端的秘密来响应即将用Firebase云功能购买产品的客户。
你可以访问该网站的部署版本,它看起来像下面的图片。
请随意玩玩这个应用,以便更好地理解我们在这篇文章中的工作内容。
设置亚马逊的克隆应用
在我们开始编码之前,首先,让我们从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服务的文件,firebase
、App.js
、Header.js
、Login.js
、Payment.js
和Orders.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
文件中的代码,在这里我们创建了创建用户的功能,并通过FirebasecreateUserWithEmailAndPassword
和signInWithEmailAndPassword
方法来签署他们。当我们扫描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
模块中导入signInWithEmailAndPassword
和createUserWithEmailAndPassword
方法,然后更新代码。重构后的版本将看起来像下面的代码。
// 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.js
和Header.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.js
和Orders.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.