Firebase Realtime Database 常用功能筆記

/

本篇要解決的問題

在寫上一篇關於 監聽頁面關閉事件 的時候,為了要測試資料有沒有送出去,有用到 Firebase Realtime Database,因為它操作起來簡單,看資料、刪資料都很方便。

原本以為以前有寫過相關的筆記文,想找來直接複製貼上 code 使用,結果找了一下後才發現,咦?原來只有寫過怎麼在 Google Apps Script 上使用,卻沒寫過怎麼在 Client 端上使用!

另外有寫過怎麼在 Client 端上使用 Firebase Cloud Firestore,但就是沒有寫過 Realtime Database!

一陣驚為天人之下,就決定整理一下常用的 Realtime Database 取資料、寫資料功能,方便以後可以複製貼上查找。

本篇主要參考的資料是官方說明文件:Firebase Realtime Database Web


建立 Realtime Database

在開始寫程式碼之前,我們要開啟 Firebase 上 Realtime Database 的功能。

進到 Firebase 後台,左側選單點選「Realtime Database」,再點擊「建立資料庫」:

即時資料庫的位置可以選一個距離比較近的,亞洲的話可以選新加坡。

選好儲存位置,下一步是選擇安全規則:

選擇安全規則
選擇安全規則

預設會是「鎖定模式」,就是全部的人都禁止讀取跟寫入資料,想改的話之後都可以改,我們直接按下「啟用」,之後會看見我們的資料庫建立完成,並且上方的頁籤是停在「資料」上:

Realtime Database 資料介面
Realtime Database 資料介面

修改安全規則

頁面點選上圖頁籤的「規則」,會進到修改安全規則的頁面:

修改安全規則
修改安全規則

完整安全規則的設定可以看官方文件:Basic Security Rules

這邊整理最常用到的 5 種情況:

Locked Mode 鎖定模式,全部阻擋

鎖定模式是預設的模式,所有人一律禁止讀寫資料:

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

全部開放

跟鎖定模式相反,把 false 都改為 true,就是開放給所有人使用:

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Test mode 測試模式,一段時間內開通讀寫功能

我們在建立資料庫時,除了鎖定模式下還有另一個測試模式可以選擇,就是限定一段時間內所有人都可以讀寫:

{
  "rules": {
    ".read": "now < 1638892800000",  // 2021-12-8
    ".write": "now < 1638892800000",  // 2021-12-8
  }
}

1638892800000 那一串數字是毫秒,取得方式很簡單,JS 中 console.log 以下就可以取得:

new Date('2021-12-8').getTime()

2021-12-8 換成我們想要的日期即可。

All authenticated users 有登入帳號即可使用

所謂的「有登入帳號」,是指有登入 Firebase 的帳號,Firebase 必須開通 Auth 功能,並且由此功能登入帳號。

怎麼使用 Firebase 的登入功能可看以下二篇:

Firebase Authentication 第三方登入 – Google、FB

Firebase Authentication 第三方登入 – GitHub

{
  "rules": {
    ".read": "auth.uid != null",
    ".write": "auth.uid != null"
  }
}

用 Firebase 的登入功能登入後,Firebase 就會派一組 uid 給使用者,因此 uid 就不會是 null

限定自己的帳號可以讀寫資料

這段是改寫上面的「有登入帳號即可使用」,把 null 改成我們的 uid

{
  "rules": {
    ".read": "auth.uid == '我們的 uid'",
    ".write": "auth.uid == '我們的 uid'"
  }
}

我們的 uid 在 Firebase 後台中可以看到,如圖:

找到我們的 uid
找到我們的 uid

所以我們就可以寫成:

{
  "rules": {
    ".read": "auth.uid == 'IcygiRAG5KYfgGiwjqOaEQuT1122'",
    ".write": "auth.uid == 'IcygiRAG5KYfgGiwjqOaEQuT1122'"
  }
}

安裝 Firebase SDK

Firebase SDK 在 Web 的版本上,寫這篇時是 V9,主推用模組整合工具,用 npm 或 yarn 安裝 Package 後,用 Webpack 等打包使用。

August 使用的工具是 CodeKit,可以直接使用 export、import,相當方便。

先在我們的檔案中安裝 Firebase 的 Package,資料夾中打開終端機,輸入:

npm install firebase

// 或

yarn add firebase

安裝好 Firebase Package 後,接著要引用 SDK,引用的程式碼在 Firebase 主控台的「專案總覽 > 專案設定 > 網頁應用程式」頁面中的看到,再加上 Realtime Database 的部份,程式碼整理如下:

import { initializeApp } from 'firebase/app';
import { getDatabase } from "firebase/database";

const firebaseConfig = {
  apiKey: "{{apiKey}}",
  authDomain: "{{projectId}}.firebaseapp.com",
  databaseURL: "https://{{databaseName}}.firebaseio.com",
  storageBucket: "{{bucket}}.appspot.com"
};

const app = initializeApp(firebaseConfig);
const database = getDatabase(app);

其中 import { getDatabase } from "firebase/database" 這行,會因為我們之後要使用的功能不一樣,引用的功能也會做增加。

{{apiKey}}{{projectId}}{{bucket}} 這三個要替換成自己使用的 Firebase 專案設定值。


寫入 / 更新資料

在讀取資料前,先來塞個幾筆資料進 DB 吧~

寫入、更新資料看文件上分為這二種:

  • 寫入:set
  • 更新:update

但,不知道是不是哪裡寫錯,這二個用起來的結果是一樣的啊 = =

Realtime Database 的文件上看不到 Cloud Firestore 上有的 merge 參數,可以只更新有變動的部份。

文件上的 update 可以做到不覆蓋掉原本資料的方式,就是建一個亂數的序號當資料的 key,在這 key 裡塞資料。

嗯…但試了一下,在 set 時用一樣的方式,也是直接新增而不會覆蓋掉原本的資料啊 = =

可能是 August 誤會了什麼,如果有知道誤會地方的大大歡迎留言提供。

因為 August 實測 setupdate 出的結果都一樣,因此程式碼就寫成以下一段:

import {
  getDatabase, ref, set, update, push, child
} from "firebase/database";
const db = getDatabase();

// set 寫入資料
function setData() {
  // 建立一組亂數序號
  const newKey = push(child(ref(db), 'users')).key;
  
  // 寫入資料
  set(ref(db, 'users/' + newKey), {
    username: 'set-username',
  });
}

// update 更新資料
function updateData() {
  // 建立一組亂數序號
  const newKey = push(child(ref(db), 'users')).key;
  
  // 更新資料
  update(ref(db), {
    ['users/' + newKey]: {
      username: 'update-username'
    }
  });
}

執行 setData()updateData() 後,資料庫上會看到像這樣:

寫入資料
寫入資料

可以看到都是把資料塞進到新建的亂數 key 下,避免讓原有資料被整個覆蓋。


讀取資料

讀取數據分為二種狀況:自動監聽數據的變化、只讀取一次數據。

自動監聽數據的變化

頁面除了抓一次 Realtime Database 上的資料下來,當 DB 的資料有變動時,會自動再抓一次新資料。

import { getDatabase, ref, onValue } from "firebase/database";
const db = getDatabase();
const dbRef = ref(db, '/users/');

onValue(dbRef, snapshot => {
  console.log(snapshot.val());
});

Realtime Database 就是即時資料庫,當資料有變動時自動重抓一次資料,很適合做成像聊天室之類的功能。

只讀取一次數據

看文件,只讀取一次數據下來有二種方式。

優先從本地的 Cache 抓資料

import { getDatabase, ref, onValue } from "firebase/database";
const db = getDatabase();
const dbRef = ref(db, '/users/');

onValue(dbRef, (snapshot) => {
  console.log(snapshot.val());
}, {
  onlyOnce: true
});

直接從 Server 上抓資料

import { getDatabase, ref, get, child } from "firebase/database";
const db = getDatabase();
const dbRef = ref(getDatabase());

get(child(dbRef, 'users/')).then((snapshot) => {
  if(snapshot.exists()) {
    console.log(snapshot.val());
  } else {
    console.log('沒有資料');
  }
  }).catch((error) => {
    console.error(error);
  });

刪除資料

刪除資料的方式有二種,一種是使用 remove(),另一種是直接把值設為 null

我們假設在 DB 上的資料是這樣:

user: {
  a: { key: value },
  b: { key2: value2 }
}

我們要刪掉 user.a 的資料,二種刪除資料的方式如下:

import {
  getDatabase, ref, update, set, remove
} from "firebase/database";
const db = getDatabase();

// remove
remove(ref(db, 'users/a'));

// 把資料改為 null:update
update(ref(db), {
  ['users/a']: null
})
  
// 把資料改為 null:set
set(ref(db, 'users/a'), null);

關於離線處理

看了文件上說,即便使用者離線,Firebase 還是會運作,試了一下還真的可以,不過僅限於頁籤還沒關閉的狀態下。

看了一下 Network,發現 Realtime Database 是用 WebSocket 的,不知道是不是因為這樣,所以離線時還能處理數據?

因為看 維基 上面寫:

在WebSocket API中,瀏覽器和伺服器只需要完成一次交握,兩者之間就可以建立永續性的連接,並進行雙向資料傳輸。

同場加映,之前寫過的 WebSocket 筆記文:WebSocket 基本介紹及使用筆記

總之,Realtime Database 有提供監測使用者離線時的事件。

以下是當使用者離線或斷線時,寫一筆資料到 DB 上的程式碼:

import {
  getDatabase, ref, set, onDisconnect
} from "firebase/database";

const db = getDatabase();
const dfRef = ref(db, 'disconnectmessage');
onDisconnect(dbRef).set('離線囉~');
Summary
Firebase Realtime Database 常用功能筆記
Article Name
Firebase Realtime Database 常用功能筆記
Description
探索 Firebase Realtime Database 的核心功能和應用。學習如何建立、配置和操作即時數據庫,實現數據的即時讀寫、更新和離線處理。本文提供了詳細的操作指南和範例,是開發者的實用資源。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

0 Comments
Inline Feedbacks
看所有留言

Let's Write

前端工程師 August 的學習筆記 — solving problems, in simple ways.

Follow us Telegram GitHub