owned this note
owned this note
Published
Linked with GitHub
---
tags: 前端
---
# Firebase(Netlify, Heroku, Linode)
隨著App的開發者群族的增長、流程模式逐漸標準化,後端架構可以自動化,如資料庫建立、授權驗證、手機簡訊發送、專案部署、SSL憑證、網域設定等等,唯獨前端部分必須客製,因此Firebase公司隨即成立,被Google發現其龐大的潛在商機而收購,其中Firebase提供的服務包含
1. Hosting 部屬託管
2. Database NoSQL資料庫
3. Storage 檔案儲存託管
4. Auth 授權與驗證
5. Functions 後端功能與執行
### Hosting
|動作|指令|
|-|-|
|登入(初次登入會彈出網頁)|firebase login|
|初始化設定(選擇使用的功能)|firebase init|
|在本地端運行測試|firebase serve|
|正式部署(採用git上傳的方式)|firebase deploy|
> init hosting的時候詢問的問題
1. Are you ready to proceed?(是)
2. Which Firebase CLI features...(下移至Hosting按空白鍵)
3. What do you want to use as your public directory?(輸入打包好的資料夾名稱)
4. Configure as a single-page app?(如果是SPA的話選y,其餘選N)
5. File dist/index.html already exists. Overwrite?(當然不要)
> 同時部屬多語言網站
```
修改firebase.json
"hosting":[{
target: "zh-tw",
public: "dist/zh-tw",
...
}]
```
指令`firebase target:apply hosting zh-tw 域名`
> 新增網域
1. 在主控台>Hosting主頁上點擊新增網域
2. 到namecheap管理頁
3. 編輯domain name
4. txt record 並等待幾個小時來驗證生效
5. A record - firebase console提供的IP位址
### Database
```javascript=
import firebase from 'firebase/app'
import 'firebase/firestore'
const config = {
apiKey: "123456789",
authDomain: "my-project-123456789.firebaseapp.com",
databaseURL: "https://my-project-123456789.firebaseio.com",
projectId: "my-project-123456789",
storageBucket: "my-project-123456789.appspot.com",
messagingSenderId: "123456789"
}
firebase.initializeApp(config);
const db = firebase.firestore();
```
> 新增
```javascript=
db.collection('users').add({name:'bang',city:'gang'}).then(docRef=>docRef.id)
db.collection('users').doc('chuboy').set({bang:'bang'})
```
> 讀取
|動作|Query|
|-|-|
|篩選(比較)|where(item,'==','objectKey')|
|篩選(包含)|where(items,'array-contains','objectKey')|
|排序|orderBy() orderByKey() Child Value|
|選前幾筆|limit() limitToFirst() limitToLast()|
|比對篩選(替代offset)|startAt(), endAt(), equalTo()|
```javascript=
db.collection('users').get().then(snapshot=>{
// 回傳一個物件(而非陣列),裡面有empty屬性與forEach函式
if(!snapshot.empty){
snapshot.forEach(doc=>{
docId = doc.id;
data = doc.data();
})
}
})
// 自定義一個snapshot轉array
function s2a(s){let a=[];s.forEach(doc=>a.push({id:doc.id,...doc.data()}));return a}
// 自定義一個群組化,arr-obj轉obj-arr-obj
function groupBy(xs, key){return xs.reduce((rv, x)=> {(rv[x[key]]=rv[x[key]]||[]).push(x);return rv;},{})}
db.collection('users').doc('chuboy').get().then(doc=>{
// 回傳一個物件,裡面有exists、id屬性與data函式
if(doc.exists){
docId = doc.id //docId=='chuboy'
data = doc.data()
}
})
```
> 更新
```javascript=
ref.doc(id).update({phone:'9487946'})//更改
ref.doc(id).set({city:'gangster'})//覆蓋掉整個doc
```
> 刪除
```javascript=
ref.doc(id).delete()
```
> 與伺服器對話(偵測新增與刪除事件)
```javascript=
query.onSnapshot(snapshot=>{
let changes = snapshot.docChanges();
changes.forEach(change=>{
if(change.type=='added'){
change.doc.data()
}else if(change.type=='removed'){
change.doc.id
}
})
})
```
### Storage
```javascript=
<input type="file" onchange="uploadFile(this.files)">
<img id='img'>
// 上傳完畢之後才會產生一個下載網址
function uploadFile(files){
const storageRef = firebase.storage().ref().child('horse.jpg')
storageRef.put(files[0]).then(snapshot=>{
snapshot.ref.getDownloadURL().then(downloadURL=>{
document.querySelector('#img').src = downloadURL;
})
})
}
```
### Auth
```javascript=
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider)
.then(result=>{
const user = result.user;
document.write(user.displayName);
})
login(){
firebase.auth().signInWithEmailAndPassword(this.email,this.password).then(
(user)=>{
alert('Well done ! You are now login'+user);
this.$router.replace('home')
},
(err)=>{alert('Oops '+err.message)}
)
signup(){
firebase.auth().createUserWithEmailAndPassword(this.email,this.password).then(
(user)=>{
alert('Your account '+user+' has been created');
this.$router.replace('login');
},
(err)=>{alert('Oops '+err.message)}
)
logout(){
firebase.auth().signOut().then(()=>{
// set user login status
})
}
```
### Functions
Functions開啟了後端邏輯執行可能性,從中引入核心功能也可以使用第三方程式庫
```javascript=
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp();
exports.updateProgress = functions.firestore
.document('survey/{survey_id}')
.onCreate((snap, context) => {
var newValue = snap.data();
var routeID = newValue.route
var ref = admin.firestore().collection('progress').doc(routeID)
return ref.get().then(s=>{
var doc = s.data()
doc.sum+= parseFloat(newValue.score)
doc.count+=1;
return ref.set(doc)
})
});
```
```javascript=
npm i @google-cloud/storage sharp fs-extra
import * as functions from 'firebase-functions'
import * as Storage from '@google-cloud/storage'
const gcs = Storage()
import { tmdir } from 'os'
import {join, dirname} from 'path'
import * as sharp from 'sharp'
import * from 'fs-extra'
export const generateThumbs = functions.storage
.object()
.onFinalize(async object=>{
const bucket = gcs.bucket(object.bucket);
const filePath = object.name;
const fileName = filePaht.split('/').pop();
const workingDir = join(tmpdir(), 'thumbs');
const tmpFilePath = join(workingDir,'source.png');
if(filename.includes('thumb@')||!object.contentType.includes('image')){
console.log('existing function')
return false
}
// 1. Ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Source File
await bucket.file(filePath).download({
destination: tmpFilePath
})
// 3. Resize the images and define an array of upload promises
const sizes = [64,128,256];
const uploadPromises = sizes.map(async size=>{
const thumbName = `thumb@${size}_${fileName}`;
const thumbPath = join(workingDir, thumbName);
// Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
// Upload to GCS
return bucket.upload(thumbPath, {
destination: join(bucketDir, thumbName)
});
});
// 4. Run the upload operations
await Promise.all(uploadPromises);
// 5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
})
firebase deploy --only functions
```