Firebase Realtime Database 常用功能筆記

Firebase Realtime Database 常用功能筆記
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
看所有留言