了解 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();
          // Subcribe 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

了解更多

進展如何?

我們很樂意您的反饋!請在此處填寫一份(非常)簡短的表格。