了解 Firebase 网页版

1. 概述

在此 Codelab 中,您将学习Firebase创建交互式 Web 应用程序的一些基础知识。您将使用多种 Firebase 产品构建活动 RSVP 和留言簿聊天应用。

此步骤的屏幕截图

你将学到什么

  • 使用 Firebase 身份验证和 FirebaseUI 对用户进行身份验证。
  • 使用 Cloud Firestore 同步数据。
  • 编写 Firebase 安全规则来保护数据库。

你需要什么

  • 您选择的浏览器,例如 Chrome。
  • 访问stackblitz.com (无需帐户或登录)。
  • Google 帐户,例如 Gmail 帐户。我们建议您使用已用于 GitHub 帐户的电子邮件帐户。这允许您使用 StackBlitz 中的高级功能。
  • Codelab 的示例代码。请参阅下一步以了解如何获取代码。

2.获取起始码

在此 Codelab 中,您将使用StackBlitz构建一个应用,这是一个集成了多个 Firebase 工作流程的在线编辑器。 Stackblitz 不需要安装软件或特殊的 StackBlitz 帐户。

StackBlitz 可让您与其他人共享项目。拥有您的 StackBlitz 项目 URL 的其他人可以查看您的代码并分叉您的项目,但他们无法编辑您的 StackBlitz 项目。

  1. 转到此 URL 获取起始代码: https://stackblitz.com/edit/firebase-gtk-web-start
  2. 在 StackBlitz 页面顶部,单击Fork

此步骤的屏幕截图

您现在拥有了一份起始代码的副本作为您自己的 StackBlitz 项目,该项目具有唯一的名称和唯一的 URL。您的所有文件和更改都保存在此 StackBlitz 项目中。

3.编辑活动信息

此 Codelab 的起始材料提供了 Web 应用程序的一些结构,包括应用程序的一些样式表和几个 HTML 容器。稍后在此 Codelab 中,您会将这些容器连接到 Firebase。

首先,让我们先熟悉一下 StackBlitz 界面。

  1. 在 StackBlitz 中,打开index.html文件。
  2. 找到event-details-containerdescription-container ,然后尝试编辑一些事件详细信息。

当您编辑文本时,StackBlitz 中的自动页面重新加载会显示新事件的详细信息。很酷,是吗?

<!-- ... -->

<div id="app">
  <img src="..." />

  <section id="event-details-container">
     <h1>Firebase Meetup</h1>

     <p><i class="material-icons">calendar_today</i> October 30</p>
     <p><i class="material-icons">location_city</i> San Francisco</p>

  </section>

  <hr>

  <section id="firebaseui-auth-container"></section>

  <section id="description-container">
     <h2>What we'll be doing</h2>
     <p>Join us for a day full of Firebase Workshops and Pizza!</p>
  </section>
</div>

<!-- ... -->

您的应用程序的预览应该如下所示:

应用预览

此步骤的屏幕截图

4. 创建并设置 Firebase 项目

显示活动信息对您的客人来说非常有用,但仅显示活动对任何人来说都没有多大用处。让我们向这个应用程序添加一些动态功能。为此,您需要将 Firebase 连接到您的应用。要开始使用 Firebase,您需要创建并设置一个 Firebase 项目。

创建 Firebase 项目

  1. 登录Firebase
  2. 在 Firebase 控制台中,单击添加项目(或创建项目),然后将您的 Firebase 项目命名为Firebase-Web-Codelab

    此步骤的屏幕截图

  3. 单击项目创建选项。如果出现提示,请接受 Firebase 条款。在 Google Analytics 屏幕上,单击“不启用”,因为您不会为此应用程序使用 Analytics。

要了解有关 Firebase 项目的更多信息,请参阅了解 Firebase 项目

在控制台中启用和设置 Firebase 产品

您正在构建的应用使用了多种可用于网络应用的 Firebase 产品:

  • Firebase 身份验证Firebase UI可让您的用户轻松登录您的应用。
  • Cloud Firestore将结构化数据保存在云端,并在数据发生变化时获得即时通知。
  • Firebase 安全规则可保护您的数据库。

其中一些产品需要特殊配置或需要使用 Firebase 控制台启用。

启用电子邮件登录以进行 Firebase 身份验证

为了允许用户登录 Web 应用,您将在此 Codelab 中使用电子邮件/密码登录方法:

  1. 在 Firebase 控制台的左侧面板中,单击“构建” > “身份验证” 。然后单击开始。您现在位于身份验证仪表板中,您可以在其中查看注册用户、配置登录提供商以及管理设置。

    此步骤的屏幕截图

  2. 选择“登录方法”选项卡(或单击此处直接转至该选项卡)。

    此步骤的屏幕截图

  3. 单击提供商选项中的电子邮件/密码,将开关切换至启用,然后单击保存

    此步骤的屏幕截图

设置 Cloud Firestore

Web 应用程序使用Cloud Firestore保存聊天消息并接收新的聊天消息。

设置 Cloud Firestore 的方法如下:

  1. 在 Firebase 控制台的左侧面板中,单击“构建” > “Firestore 数据库” 。然后单击创建数据库
  2. 单击创建数据库

    此步骤的屏幕截图

  3. 选择“以测试模式启动”选项。阅读有关安全规则的免责声明。测试模式保证您在开发过程中可以自由地写入数据库。点击下一步

    此步骤的屏幕截图

  4. 选择数据库的位置(您可以使用默认值)。但请注意,此位置以后无法更改。

    此步骤的屏幕截图

  5. 单击“完成”

5.添加并配置Firebase

现在您已经创建了 Firebase 项目并启用了一些服务,您需要告诉代码您要使用 Firebase,以及要使用哪个 Firebase 项目。

添加 Firebase 库

为了让您的应用程序使用 Firebase,您需要将 Firebase 库添加到应用程序。有多种方法可以执行此操作,如Firebase 文档 中所述。例如,您可以从 Google 的 CDN 添加库,也可以使用 npm 在本地安装它们,然后将它们打包到您的应用程序中(如果您使用的是 Browserify)。

StackBlitz 提供自动捆绑,因此您可以使用 import 语句添加 Firebase 库。您将使用库的模块化 (v9) 版本,这有助于通过称为“tree shake”的过程减小网页的整体大小。您可以在文档中了解有关模块化 SDK 的更多信息。

要构建此应用,您可以使用 Firebase 身份验证、FirebaseUI 和 Cloud Firestore 库。对于此 Codelab, index.js文件的顶部已包含以下导入语句,我们将从每个 Firebase 库导入更多方法:

// Import stylesheets
import './style.css';

// Firebase App (the core Firebase SDK) is always required
import { initializeApp } from 'firebase/app';

// Add the Firebase products and methods that you want to use
import {} from 'firebase/auth';
import {} from 'firebase/firestore';

import * as firebaseui from 'firebaseui';

将 Firebase Web 应用添加到您的 Firebase 项目

  1. 返回 Firebase 控制台,点击左上角的项目概述导航到项目的概述页面。
  2. 在项目概述页面的中心,单击 Web 图标网络应用程序图标创建一个新的 Firebase Web 应用。

    此步骤的屏幕截图

  3. 使用昵称Web App注册该应用程序。
  4. 对于此 Codelab,请勿选中Also set up Firebase Hosting for this app旁边的复选框。您现在将使用 StackBlitz 的预览窗格。
  5. 单击注册应用程序

    此步骤的屏幕截图

  6. Firebase 配置对象复制到剪贴板。

    此步骤的屏幕截图

  7. 单击继续控制台。将 Firebase 配置对象添加到您的应用程序:
  8. 返回 StackBlitz,转到index.js文件。
  9. 找到Add Firebase project configuration object here注释行,然后将配置片段粘贴到注释下方。
  10. 添加initializeApp函数调用以使用您独特的Firebase项目配置来设置Firebase。
    // ...
    // Add Firebase project configuration object here
    const firebaseConfig = {
      apiKey: "random-unique-string",
      authDomain: "your-projectId.firebaseapp.com",
      databaseURL: "https://your-projectId.firebaseio.com",
      projectId: "your-projectId",
      storageBucket: "your-projectId.appspot.com",
      messagingSenderId: "random-unique-string",
      appId: "random-unique-string",
    };
    
    // Initialize Firebase
    initializeApp(firebaseConfig);
    

6.添加用户登录(RSVP)

现在您已将 Firebase 添加到应用中,您可以设置一个 RSVP 按钮来使用Firebase 身份验证注册人员。

使用电子邮件登录和 FirebaseUI 对您的用户进行身份验证

您需要一个 RSVP 按钮来提示用户使用其电子邮件地址登录。您可以通过将FirebaseUI连接到 RSVP 按钮来完成此操作。FirebaseUI 是一个库,它为您提供基于 Firebase Auth 的预构建 UI。

FirebaseUI 需要一个配置(请参阅文档中的选项)来执行两件事:

  • 告诉 FirebaseUI 您要使用电子邮件/密码登录方法。
  • 处理成功登录的回调并返回 false 以避免重定向。您不希望页面刷新,因为您正在构建单页 Web 应用程序。

添加代码以初始化 FirebaseUI Auth

  1. 在 StackBlitz 中,转到index.js文件。
  2. 在顶部,找到firebase/auth import 语句,然后添加getAuthEmailAuthProvider ,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. initializeApp之后保存对auth对象的引用,如下所示:
    initializeApp(firebaseConfig);
    auth = getAuth();
    
  4. 请注意,起始代码中已提供 FirebaseUI 配置。它已经设置为使用电子邮件身份验证提供商。
  5. index.jsmain()函数底部,添加 FirebaseUI 初始化语句,如下所示:
    async function main() {
      // ...
    
      // Initialize the FirebaseUI widget using Firebase
      const ui = new firebaseui.auth.AuthUI(auth);
    }
    main();
    
    

在 HTML 中添加 RSVP 按钮

  1. 在 StackBlitz 中,转到index.html文件。
  2. event-details-container内添加 RSVP 按钮的 HTML,如下例所示。

    请小心使用如下所示的相同id值,因为对于此 Codelab, index.js文件中已存在针对这些特定 ID 的挂钩。

    请注意,在index.html文件中,有一个 ID 为firebaseui-auth-container容器。这是您将传递给 FirebaseUI 以保存您的登录信息的 ID。
    <!-- ... -->
    
    <section id="event-details-container">
        <!-- ... -->
        <!-- ADD THE RSVP BUTTON HERE -->
        <button id="startRsvp">RSVP</button>
    </section>
    <hr>
    <section id="firebaseui-auth-container"></section>
    <!-- ... -->
    
    应用程序预览

    此步骤的屏幕截图

  3. 在 RSVP 按钮上设置侦听器并调用 FirebaseUI 启动函数。这告诉 FirebaseUI 您想要查看登录窗口。

    将以下代码添加到index.js中的main()函数底部:
    async function main() {
      // ...
    
      // Listen to RSVP button clicks
      startRsvpButton.addEventListener("click",
       () => {
            ui.start("#firebaseui-auth-container", uiConfig);
      });
    }
    main();
    

测试登录应用程序

  1. 在 StackBlitz 的预览窗口中,单击 RSVP 按钮登录应用程序。
    • 对于此 Codelab,您可以使用任何电子邮件地址,甚至可以使用虚假电子邮件地址,因为您没有为此 Codelab 设置电子邮件验证步骤。
    • 如果您看到一条错误消息,指出auth/operation-not-allowedThe given sign-in provider is disabled for this Firebase project ,请检查以确保您在 Firebase 控制台中启用了电子邮件/密码作为登录提供程序。
    应用预览

    此步骤的屏幕截图

  2. 转到 Firebase 控制台中的身份验证仪表板。在“用户”选项卡中,您应该会看到您输入的用于登录应用程序的帐户信息。

    此步骤的屏幕截图

将身份验证状态添加到 UI

接下来,确保 UI 反映您已登录的事实。

您将使用 Firebase 身份验证状态侦听器回调,只要用户的登录状态发生变化,该回调就会收到通知。如果当前有登录用户,您的应用会将“RSVP”按钮切换为“注销”按钮。

  1. 在 StackBlitz 中,转到index.js文件。
  2. 在顶部,找到firebase/auth import 语句,然后添加signOutonAuthStateChanged ,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. main()函数底部添加以下代码:
    async function main() {
      // ...
    
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
        } else {
          startRsvpButton.textContent = 'RSVP';
        }
      });
    }
    main();
    
  4. 在按钮监听器中,检查是否存在当前用户并将其注销。为此,请将当前的startRsvpButton.addEventListener替换为以下内容:
    // ...
    // Called when the user clicks the RSVP button
    startRsvpButton.addEventListener('click', () => {
      if (auth.currentUser) {
        // User is signed in; allows user to sign out
        signOut(auth);
      } else {
        // No user is signed in; allows user to sign in
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    });
    

现在,应用程序中的按钮应显示LOGOUT ,并且单击时应切换回RSVP

应用预览

此步骤的屏幕截图

7. 将消息写入 Cloud Firestore

知道用户即将到来固然很棒,但我们还是让客人在应用程序中做点其他事情吧。如果他们可以在留言簿中留言怎么办?他们可以分享为什么他们很高兴来这里或者他们希望见到谁。

要存储用户在应用中编写的聊天消息,您将使用Cloud Firestore

数据模型

Cloud Firestore 是一个 NoSQL 数据库,数据库中存储的数据分为集合、文档、字段和子集合。您将把聊天的每条消息作为文档存储在名为guestbook顶级集合中。

Firestore 数据模型图显示包含多个消息文档的留言簿集合

将消息添加到 Firestore

在本部分中,您将添加用户向数据库写入新消息的功能。首先,添加 UI 元素(消息字段和发送按钮)的 HTML。然后,添加将这些元素连接到数据库的代码。

添加消息字段和发送按钮的 UI 元素:

  1. 在 StackBlitz 中,转到index.html文件。
  2. 找到guestbook-container ,然后添加以下 HTML 以创建带有消息输入字段和发送按钮的表单。
    <!-- ... -->
    
     <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form id="leave-message">
         <label>Leave a message: </label>
         <input type="text" id="message">
         <button type="submit">
           <i class="material-icons">send</i>
           <span>SEND</span>
         </button>
       </form>
    
     </section>
    
    <!-- ... -->
    

应用预览

此步骤的屏幕截图

用户单击“发送”按钮将触发下面的代码片段。它将消息输入字段的内容添加到数据库的guestbook集合中。具体来说, addDoc方法将消息内容添加到新文档(具有自动生成的 ID)到guestbook集合中。

  1. 在 StackBlitz 中,转到index.js文件。
  2. 在顶部,找到firebase/firestore导入语句,然后添加getFirestoreaddDoccollection ,如下所示:
    // ...
    
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {
      getFirestore,
      addDoc,
      collection
    } from 'firebase/firestore';
    
  3. 现在,我们将在initializeApp之后立即保存对Firestore db对象的引用:
    initializeApp(firebaseConfig);
    auth = getAuth();
    db = getFirestore();
    
  4. main()函数的底部,添加以下代码。

    请注意, auth.currentUser.uid是对 Firebase 身份验证为所有登录用户提供的自动生成的唯一 ID 的引用。
    async function main() {
      // ...
    
      // Listen to the form submission
      form.addEventListener('submit', async e => {
        // Prevent the default form redirect
        e.preventDefault();
        // Write a new message to the database collection "guestbook"
        addDoc(collection(db, 'guestbook'), {
          text: input.value,
          timestamp: Date.now(),
          name: auth.currentUser.displayName,
          userId: auth.currentUser.uid
        });
        // clear message input field
        input.value = '';
        // Return false to avoid redirect
        return false;
      });
    }
    main();
    

仅向登录用户显示留言簿

您不希望任何人都能看到客人的聊天内容。为了确保聊天安全,您可以采取的一件事是仅允许登录用户查看留言簿。也就是说,对于您自己的应用程序,您还需要使用Firebase 安全规则来保护您的数据库。 (稍后将在 Codelab 中提供有关安全规则的更多信息。)

  1. 在 StackBlitz 中,转到index.js文件。
  2. 编辑onAuthStateChanged侦听器以隐藏和显示留言簿。
    // ...
    
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
      }
    });
    

测试发送消息

  1. 确保您已登录该应用程序。
  2. 输入一条消息,例如“嘿!”,然后单击“发送”

此操作会将消息写入您的 Cloud Firestore 数据库。但是,您还不会在实际的 Web 应用程序中看到该消息,因为您仍然需要实现数据检索。接下来你会这样做。

但您可以在 Firebase 控制台中看到新添加的消息。

在 Firebase 控制台的Firestore 数据库仪表板中,您应该会看到带有新添加消息的guestbook集合。如果您继续发送消息,您的留言簿集合将包含许多文档,如下所示:

Firebase 控制台

此步骤的屏幕截图

8. 阅读消息

同步消息

很高兴客人可以将消息写入数据库,但他们还无法在应用程序中看到它们。

要显示消息,您需要添加在数据更改时触发的侦听器,然后创建一个显示新消息的 UI 元素。

您将添加代码来侦听来自应用程序的新添加的消息。首先,在 HTML 中添加一个部分来显示消息:

  1. 在 StackBlitz 中,转到index.html文件。
  2. guestbook-container中,添加 ID 为guestbook的新部分。
    <!-- ... -->
    
      <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form><!-- ... --></form>
    
       <section id="guestbook"></section>
    
     </section>
    
    <!-- ... -->
    

接下来,注册侦听器来侦听对数据所做的更改:

  1. 在 StackBlitz 中,转到index.js文件。
  2. 在顶部,找到firebase/firestore import 语句,然后添加queryorderByonSnapshot ,如下所示:
    // ...
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot
    } from 'firebase/firestore';
    
  3. main()函数的底部,添加以下代码以循环遍历数据库中的所有文档(留言簿消息)。要了解有关此代码中发生的情况的更多信息,请阅读代码片段下面的信息。
    async function main() {
      // ...
    
      // Create query for messages
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    main();
    

为了侦听数据库中的消息,您已使用collection函数创建了对特定集合的查询。上面的代码监听guestbook集合中的更改,这是存储聊天消息的地方。消息也按日期排序,使用orderBy('timestamp', 'desc')在顶部显示最新消息。

onSnapshot函数有两个参数:要使用的查询和回调函数。当与查询匹配的文档发生任何更改时,将触发回调函数。这可能是因为消息被删除、修改或添加。有关更多信息,请参阅Cloud Firestore 文档

测试同步消息

Cloud Firestore 自动即时与订阅数据库的客户端同步数据。

  • 您之前在数据库中创建的消息应显示在应用程序中。随意写新消息;它们应该立即出现。
  • 如果您在多个窗口或选项卡中打开工作区,消息将跨选项卡实时同步。
  • (可选)您可以尝试直接在 Firebase 控制台的数据库部分手动删除、修改或添加新消息;任何更改都应显示在 UI 中。

恭喜!您正在应用中阅读 Cloud Firestore 文档!

应用预览

此步骤的屏幕截图

9. 设置基本安全规则

您最初将 Cloud Firestore 设置为使用测试模式,这意味着您的数据库开放读取和写入。但是,您应该只在开发的早期阶段使用测试模式。作为最佳实践,您应该在开发应用程序时为数据库设置安全规则。安全性应该成为应用程序结构和行为不可或缺的一部分。

安全规则允许您控制对数据库中文档和集合的访问。灵活的规则语法允许您创建匹配从对整个数据库的所有写入到对特定文档的操作的任何规则。

您可以在 Firebase 控制台中为 Cloud Firestore 编写安全规则:

  1. 在 Firebase 控制台的“构建”部分中,单击Firestore Database ,然后选择“规则”选项卡(或单击此处直接转到“规则”选项卡)。
  2. 您应该会看到以下默认安全规则,以及从今天起几周后的公共访问时间限制。

此步骤的屏幕截图

识别集合

首先,确定应用程序写入数据的集合。

  1. 删除现有的match /{document=**}子句,因此您的规则如下所示:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
      }
    }
    
  2. match /databases/{database}/documents中,标识您要保护的集合:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
         // You'll add rules here in the next step.
      }
    }
    

添加安全规则

由于您使用身份验证 UID 作为每个留言簿文档中的字段,因此您可以获取身份验证 UID 并验证尝试写入文档的任何人是否具有匹配的身份验证 UID。

  1. 将读取和写入规则添加到您的规则集中,如下所示:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
            if request.auth.uid == request.resource.data.userId;
        }
      }
    }
    
  2. 单击“发布”以部署新规则。现在,对于留言簿,只有登录用户才能阅读消息(任何消息!),但您只能使用您的用户 ID 创建消息。我们也不允许编辑或删除消息。

添加验证规则

  1. 添加数据验证以确保文档中存在所有预期字段:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
          if request.auth.uid == request.resource.data.userId
              && "name" in request.resource.data
              && "text" in request.resource.data
              && "timestamp" in request.resource.data;
        }
      }
    }
    
  2. 单击“发布”以部署新规则。

重置监听器

由于您的应用程序现在只允许经过身份验证的用户登录,因此您应该将留言簿firestore查询移至身份验证侦听器内。否则会出现权限错误,用户注销时应用会断开连接。

  1. 在 StackBlitz 中,转到index.js文件。
  2. 将 guestbook 集合onSnapshot侦听器拉入名为subscribeGuestbook的新函数中。另外,将onSnapshot函数的结果分配给guestbookListener变量。

    Firestore onSnapshot侦听器返回一个取消订阅函数,您稍后可以使用该函数取消快照侦听器。
    // ...
    // Listen to guestbook updates
    function subscribeGuestbook() {
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      guestbookListener = onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    
  3. 在下面添加一个名为unsubscribeGuestbook的新函数。检查guestbookListener变量是否不为null,然后调用该函数取消监听。
    // ...
    // Unsubscribe from guestbook updates
    function unsubscribeGuestbook() {
      if (guestbookListener != null) {
        guestbookListener();
        guestbookListener = null;
      }
    }
    

最后,将新函数添加到onAuthStateChanged回调中。

  1. if (user)底部添加subscribeGuestbook()
  2. else语句的底部添加unsubscribeGuestbook()
    // ...
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
        // Subscribe to the guestbook collection
        subscribeGuestbook();
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
        // Unsubscribe from the guestbook collection
        unsubscribeGuestbook();
      }
    });
    

10.奖励步骤:练习你所学到的知识

记录与会者的 RSVP 状态

目前,您的应用只允许人们在对活动感兴趣的情况下开始聊天。此外,您知道某人是否会来的唯一方法是他们是否在聊天中发布。让我们组织起来,让人们知道有多少人来。

您将添加一个开关来注册想要参加活动的人员,然后收集参加活动的人数。

  1. 在 StackBlitz 中,转到index.html文件。
  2. guestbook-container中,添加一组YESNO按钮,如下所示:
    <!-- ... -->
      <section id="guestbook-container">
       <h2>Are you attending?</h2>
         <button id="rsvp-yes">YES</button>
         <button id="rsvp-no">NO</button>
    
       <h2>Discussion</h2>
    
       <!-- ... -->
    
     </section>
    <!-- ... -->
    

应用预览

此步骤的屏幕截图

接下来,注册按钮点击的监听器。如果用户单击YES ,则使用其身份验证 UID 将响应保存到数据库。

  1. 在 StackBlitz 中,转到index.js文件。
  2. 在顶部,找到firebase/firestore import 语句,然后添加docsetDocwhere ,如下所示:
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot,
      doc,
      setDoc,
      where
    } from 'firebase/firestore';
    
  3. main()函数的底部,添加以下代码来监听RSVP状态:
    async function main() {
      // ...
    
      // Listen to RSVP responses
      rsvpYes.onclick = async () => {
      };
      rsvpNo.onclick = async () => {
      };
    }
    main();
    
    
  4. 接下来,创建一个名为attendees的新集合,然后在单击 RSVP 按钮时注册文档引用。根据单击的按钮将该引用设置为truefalse

    首先,对于rsvpYes
    // ...
    // Listen to RSVP responses
    rsvpYes.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attendi()ng: true
      try {
        await setDoc(userRef, {
          attending: true
        });
      } catch (e) {
        console.error(e);
      }
    };
    
    然后,对于rsvpNo相同,但值为false
    rsvpNo.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attending: true
      try {
        await setDoc(userRef, {
          attending: false
        });
      } catch (e) {
        console.error(e);
      }
    };
    

更新您的安全规则

由于您已经设置了一些规则,因此您使用按钮添加的新数据将被拒绝。

允许添加attendees集合

您需要更新规则以允许添加到attendees集合中。

  1. 对于attendees集合,由于您使用身份验证 UID 作为文档名称,因此您可以获取它并验证提交者的uid是否与他们正在编写的文档相同。您将允许每个人读取与会者列表(因为那里没有私人数据),但只有创建者才能更新它。
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId;
        }
      }
    }
    
  2. 单击“发布”以部署新规则。

添加验证规则

  1. 添加一些数据验证规则以确保文档中存在所有预期字段:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId
              && "attending" in request.resource.data;
    
        }
      }
    }
    
  2. 不要忘记单击“发布”来部署您的规则!

(可选)您现在可以查看单击按钮的结果。转到 Firebase 控制台中的 Cloud Firestore 仪表板。

读取回复状态

现在您已经记录了响应,让我们看看谁来了并将其反映在 UI 中。

  1. 在 StackBlitz 中,转到index.html文件。
  2. description-container中,添加 ID 为number-attending的新元素。
    <!-- ... -->
    
     <section id="description-container">
         <!-- ... -->
         <p id="number-attending"></p>
     </section>
    
    <!-- ... -->
    

接下来,为attendees集合注册侦听器并计算“是”响应的数量:

  1. 在 StackBlitz 中,转到index.js文件。
  2. main()函数的底部,添加以下代码来监听 RSVP 状态并计算YES点击次数。
    async function main() {
      // ...
    
      // Listen for attendee list
      const attendingQuery = query(
        collection(db, 'attendees'),
        where('attending', '==', true)
      );
      const unsubscribe = onSnapshot(attendingQuery, snap => {
        const newAttendeeCount = snap.docs.length;
        numberAttending.innerHTML = newAttendeeCount + ' people going';
      });
    }
    main();
    

最后,让我们突出显示与当前状态对应的按钮。

  1. 创建一个函数来检查当前的身份验证 UID 是否在attendees集合中具有条目,然后将按钮类设置为clicked
    // ...
    // Listen for attendee list
    function subscribeCurrentRSVP(user) {
      const ref = doc(db, 'attendees', user.uid);
      rsvpListener = onSnapshot(ref, doc => {
        if (doc && doc.data()) {
          const attendingResponse = doc.data().attending;
    
          // Update css classes for buttons
          if (attendingResponse) {
            rsvpYes.className = 'clicked';
            rsvpNo.className = '';
          } else {
            rsvpYes.className = '';
            rsvpNo.className = 'clicked';
          }
        }
      });
    }
    
  2. 另外,让我们创建一个取消订阅的函数。当用户注销时将使用该值。
    // ...
    function unsubscribeCurrentRSVP() {
      if (rsvpListener != null) {
        rsvpListener();
        rsvpListener = null;
      }
      rsvpYes.className = '';
      rsvpNo.className = '';
    }
    
  3. 从身份验证侦听器调用函数。
    // ...
    // Listen to the current Auth state
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
          // Show guestbook to logged-in users
          guestbookContainer.style.display = 'block';
    
          // Subscribe to the guestbook collection
          subscribeGuestbook();
          // Subscribe to the user's RSVP
          subscribeCurrentRSVP(user);
        } else {
          startRsvpButton.textContent = 'RSVP';
          // Hide guestbook for non-logged-in users
          guestbookContainer.style.display = 'none'
          ;
          // Unsubscribe from the guestbook collection
          unsubscribeGuestbook();
          // Unsubscribe from the guestbook collection
          unsubscribeCurrentRSVP();
        }
      });
    
  4. 尝试以多个用户身份登录,并查看每次单击“是”按钮时计数都会增加。

应用预览

此步骤的屏幕截图

11. 恭喜!

您已使用 Firebase 构建了交互式实时 Web 应用程序!

我们涵盖的内容

  • Firebase 身份验证
  • Firebase用户界面
  • 云Firestore
  • Firebase 安全规则

下一步

  • 想要了解更多 Firebase 开发人员工作流程吗?查看Firebase 模拟器 Codelab,了解如何完全在本地测试和运行您的应用。
  • 想要详细了解其他 Firebase 产品吗?也许您想存储用户上传的图像文件?或者向您的用户发送通知?查看Firebase Web Codelab ,了解更深入了解更多 Firebase Web 产品的代码实验室。
  • 想要了解有关 Cloud Firestore 的更多信息?也许您想了解子集合和交易?前往Cloud Firestore Web Codelab,获取更深入了解 Cloud Firestore 的 Codelab。或者查看此YouTube 系列以了解 Cloud Firestore

了解更多

进展如何?

我们很乐意您的反馈!请在此处填写一份(非常)简短的表格。