Firebase 存在於這世上已經有幾年的時間了,後來有併入到 Google 的旗下。而這一年來 Firebase 的功能與服務,不論是威力或是廣度都遠比剛出道時,來的強大許多。
這一次透過寫一個 side project 來重新探索 Firebase 的威力
環境準備
※ 這裡預先假設你已經有一個 Angular 的專案,如果沒有的話,可以透過 Angular CLI 產生
※ 於 Firebase 的後臺管理建立一個新的專案
Angular 有一個套件 Angularfire2
,將 Firebase 會用到的功能包起來,能讓我們簡單的使用. 安裝使用方式如下
-
安裝 Angularfire2
1
npm install firebase angularfire2 --save
-
取得 Firebase 專案的設定檔
複製紅色框起來的設定檔的部分
-
將
AngularFireModule
加到app.module.ts
裡1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18export const config = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "..."
};
({
...
imports: [
BrowserModule,
AngularFireModule.initializeApp(config),
],
bootstrap: [AppComponent]
})
export class AppModule {}- 也可以將
config
的部分放到environement
檔案內做管理
- 也可以將
-
完成基本設定
Firebase 常用功能
Firebase 除了提供 Realtime 資料庫外,也多了 Authentication
、Cloud Firestore
、Hosting
、Stroage
與 Functions
的服務,這裡就先介紹 Authentication
、Cloud Firestore
、Hosting
與 Functions
在開始之前,要先安裝 firebase-tools,這工具可以協助我們開發及部屬 Firebase 的功能
安裝 Firebase Tools
-
安裝 Firebase-tools (如果之前有裝過,這個步驟可以跳過)
1
npm install -g firebase-tools
-
到 Angular 專案下,執行
firebase init
進行第一次環境初始化設定,跟著畫面上的步驟依序執行,完成後就會產生一個.firebase.json
與.firebaserc
的檔案,分別記載相關的環境參數供後續的部屬使用
Authentication
使用者登入授權是一件很麻煩的事情,尤其是要使用各種方式登入,例如使用 Google、GitHub、Facebook 等帳號登入,光是串聯這一些就會有想放棄的念頭,好加在 Firebase 的 authentication 幫我們處理這一塊的事情,我們只需要將環境參數設定完,就可以直接使用
於 Authentication 的區塊有四個選項
-
使用者:目前有登入註冊到 Firebase 專案下的使用者有哪些
-
登入方式:設定要連接的授權服務
- 設定要使用的登入授權方式
- 紀錄允許使用 Authentication 的網域名稱
-
範本:寄信通知的內容範本
-
用量:統計
電話驗證實例
的使用量
所以後台的畫面就這些,而要如何開啟個服務的登入授權呢,基本上裡面的設定步驟都寫得很詳細,跟著做就不會錯
回到 Angular 程式內,我們要怎麼寫才能使用這些服務呢? 基本寫法如下
-
注入
AngularFireAuthModule
到AppModule
內 -
建立一個 service 或是直接在 component 寫都可以
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import { Injectable } from '@angular/core';
import { AngularFireAuth, AngularFireAuthProvider } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { User } from '@firebase/auth-types';
import { Router } from '@angular/router';
()
export class AuthService {
authState = this.afAuth.authState;
constructor(public afAuth: AngularFireAuth, private router: Router) {}
// 使用匿名登入
signInAnonymously() {
return this.afAuth.auth.signInAnonymously()
.then(this.redirectToPopup());
}
// 使用 Google 登入
signInWithGoogle() {
return this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider())
.then(this.redirectToPopup());
}
// 使用 GitHub 登入
signInWithGithub() {
return this.afAuth.auth.signInWithPopup(new firebase.auth.GithubAuthProvider())
.then(this.redirectToPopup());
}
// 登出
signOut() {
this.afAuth.auth.signOut();
}
private redirectToPopup() {
return () => this.router.navigate(['/popup']);
}
}- 基本上各服務商的登入寫法都差不多,詳細的設定可以參考這裡
-
那要怎麼判斷使用者有沒有登入呢?
1
2
3this.authService.authState.subscribe(user => {
// user 登入資訊物件
});如果使用者有登入的話,
user
就會包含相關的登入資訊,如果沒有就會收到null
值
以上就是基本的 Authentication 的用法
Database
Firebase 有兩種資料庫類型,但皆屬於 NoSQL 類型的資料庫,可是兩者的應用情境是不相同的
- Realtime Database:為所有連結的用戶端即時存儲及同步處理資料
- Cloud Firestore:新一代即時資料庫擁有更強大的查詢撼動調整資源配置功能
兩者的比較表可參考說明文件
這裡只會介紹 Firestore
資料庫
Firestore
Firestore 主要分為兩種類型的資料,document
與 collection
,顧名思義 document
就是單一筆紀錄,而 collection
是包含許多 documents
collection
對於每一個 document
都會有一個 id 的鍵值,用來讀取之用,這一個 ID 可以是自己建立,或是由 firebase 產生給你,須為唯一值
document
除了 field
外,裡面還可以建立 collection
,每一個 document 的檔案大小是有限制的,請參閱說明文件
欄位型別有這些
資料預設的排序順序為
- Null values
- Boolean values
- Integer and floating-point values, sorted in numerical order
- Date values
- Text string values
- Byte values
- Cloud Firestore references
- Geographical point values
- Array values
- Map values
關於資料庫的設計規劃方式,可能要留在以後的文章做討論了
Angular 程式
Angular 內要如何操作 collection 和 document 呢? 首先要先 import AngularFirestoreModule
到 AppModule
裡
基本操作
- Collection
- 基本撈資料的方式
1 | import { Component } from '@angular/core'; |
-
新增 document 至 collection 中
1
2
3addItem(item: Item) {
this.itemsCollection.add(item);
}- 如果要更新 document 裡面的某一筆紀錄時,則需要針對 document 做操作,這部分會在下面做說明
- Document 的操作
-
讀取特定的 document
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17({
selector: 'app-root',
template: `
<div>
{{ (item | async)?.name }}
</div>
`
})
export class AppComponent {
private itemDoc: AngularFirestoreDocument<Item>;
item: Observable<Item>;
constructor(private afs: AngularFirestore) {
this.itemDoc = afs.doc<Item>('items/1');
this.item = this.itemDoc.valueChanges();
}
} -
新增
1
2
3create(item: Item){
this.itemDoc = afs.doc<Item>('items/1').set(item)
} -
修改
1
2
3update(item: Item) {
this.itemDoc.update(item);
} -
刪除
1
2
3delete(item: Item) {
this.itemDoc.delete();
} -
新增或修改
1
2
3createOrUpdate(item: Item){
this.itemDoc = afs.doc<Item>('items/1').set(item,{ merge: true})
}
valueChanges
V.S. snapshotChanges
一般正常使用時,使用 valueChanges
已經足夠了,但如果我們需要獲取到更多資訊,例如 document 的 ID 時,這時候就得透過 snapshotChanges
才可以取得,以下示範如何取得 document ID
1 | import { Component } from '@angular/core'; |
回傳型別介面
-
valueChanges:回傳所定義的 document 型別
-
snapshotChanges
1
2
3
4
5
6
7
8interface DocumentSnapshot {
exists: boolean;
ref: DocumentReference;
id: string;
metadata: SnapshotMetadata;
data(): DocumentData;
get(fieldPath: string): any;
} -
stateChanges、auditTrail
1
2
3
4
5
6
7
8
9
10
11
12interface DocumentChangeAction {
//'added' | 'modified' | 'removed';
type: DocumentChangeType;
payload: DocumentChange;
}
interface DocumentChange {
type: DocumentChangeType;
doc: DocumentSnapshot;
oldIndex: number;
newIndex: number;
}
更多的資訊可參閱此文件
進階查詢
在查詢資料時,當然可以使用 Firebase 所提供的查詢方式,配合使用。當然操作並不能像 SQL 一樣的有彈性,所以在規劃如何存放資料時,同時也要思考要如才能查詢到自己想要的資料
1 | afs.collection('items', ref => ref.where('size', '==', 'large')) |
查詢的條件就放在 collection 的第二個參數的地方。
如果要組合多種的查詢條件時,可以這樣子寫
1 | afs.collection('items', ref => ref.where('size', '==', 'large') |
但還是有很多限制,詳細的說明,可參閱官網文件,務必要詳讀,官網文件內提出很多不能使用得查詢組合
以下是可使用的查詢方法
method | purpose |
---|---|
where |
Create a new query. Can be chained to form complex queries. |
orderBy |
Sort by the specified field, in descending or ascending order. |
limit |
Sets the maximum number of items to return. |
startAt |
Results start at the provided document (inclusive). |
startAfter |
Results start after the provided document (exclusive). |
endAt |
Results end at the provided document (inclusive). |
endBefore |
Results end before the provided document (exclusive). |
Hosting
透過 firebase tools 建立專案環境時,就會填入一些相關的資訊,例如,要上傳的網站檔案的資料夾位置,是否為 SPA 網站等資訊,當這些都設定完成後,可以透過一行指令即可完成網站部屬動作
1 | firebase deploy --only hosting |
管理後台也會有相關的部屬/用量紀錄,當然也可以綁定自己的網域名稱
Functions
什麼是 Functions ? Firebase 的 Functions 可以針對 Firebase 服務行為而被觸發的小程序,例如我希望當 Cloud Firestore 有新增資料時,幫我將某些資料整理到另外一個 collection 裡面,這時候,就可以透過 functions
來幫忙處理,這裡就簡單地提供個範例做參考
1 | import * as functions from 'firebase-functions'; |
程式碼說明
-
exports.<
> 這個會顯示在後台的 functions 列表中 -
functions.firestore.document('videoDetails/{videoId}/shareBy/{shareId}').onCreate
:當某一個 document 或是 collection 發生create
,update
,delete
, andwrite
事件時 (這裡可以指定觸發事件) -
取得網址變數
1
2可透過 { params } 的方式設定變數名稱
const videoId = context.params.videoId;
-
在之後的程式碼就寫要執行的動作
-
admin,可以不受權限控制存取 firebase 服務
1
2
3
4
5
6
7
8import * as admin from 'firebase-admin';
admin.initializeApp();
...
admin.firestore()
.collection('videos')
.doc(videoId);
Recap
Firebase 的功能很多也很強大,這裡沒有辦法全部都介紹到,例如權限的控制、檔案上傳的部分
如果想要快速建立出產品的試水溫,又不想要搞一堆後端的基礎建設,Firebase 是一個不錯的選擇