文档
Firestore Hello World:实时聊天消息
目标
连接 Firestore,创建聊天消息集合并实现实时监听——新增消息自动出现在客户端。
完整代码
Web 版 (JavaScript)
<!DOCTYPE html>
<html>
<head>
<title>Firestore 聊天室</title>
</head>
<body>
<h2>实时聊天室</h2>
<div id="messages" style="height:300px;overflow-y:auto;border:1px solid #ccc;padding:10px;margin-bottom:10px;"></div>
<input id="msgInput" placeholder="输入消息..." style="width:300px;">
<button onclick="sendMessage()">发送</button>
<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js';
import { getFirestore, collection, addDoc, query, orderBy, limit, onSnapshot, serverTimestamp } from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore.js';
// == 1. 初始化 ==
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
// == 2. 发送消息 ==
window.sendMessage = async function() {
const input = document.getElementById('msgInput');
const text = input.value.trim();
if (!text) return;
await addDoc(collection(db, 'messages'), {
text: text,
user: '匿名用户',
createdAt: serverTimestamp()
});
input.value = '';
};
// == 3. 实时监听 ==
const messagesRef = collection(db, 'messages');
const q = query(messagesRef, orderBy('createdAt', 'desc'), limit(50));
onSnapshot(q, (snapshot) => {
const container = document.getElementById('messages');
container.innerHTML = '';
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
const msg = change.doc.data();
const div = document.createElement('div');
div.textContent = `[${msg.user}] ${msg.text}`;
container.prepend(div);
}
});
});
</script>
</body>
</html>
Node.js 版
// npm install firebase-admin
const { initializeApp, cert } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
initializeApp({ credential: cert('./serviceAccountKey.json') });
const db = getFirestore();
// 写入消息
async function sendMessage(user, text) {
const docRef = await db.collection('messages').add({
user: user,
text: text,
createdAt: FieldValue.serverTimestamp(),
});
console.log(`消息 ID: ${docRef.id}`);
return docRef;
}
// 实时监听(服务端订阅)
function listenForMessages() {
const unsubscribe = db.collection('messages')
.orderBy('createdAt', 'desc')
.limit(10)
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === 'added') {
const msg = change.doc.data();
console.log(`[新消息] ${msg.user}: ${msg.text}`);
}
});
});
// 取消监听:unsubscribe();
}
// 查询历史
async function getMessages(limit = 20) {
const snapshot = await db.collection('messages')
.orderBy('createdAt', 'desc')
.limit(limit)
.get();
const messages = [];
snapshot.forEach(doc => {
messages.push({ id: doc.id, ...doc.data() });
});
return messages;
}
// 运行
sendMessage('Alice', '大家好!');
getMessages().then(msgs => console.log(msgs));
Python 版
# pip install firebase-admin
import firebase_admin
from firebase_admin import credentials, firestore
from google.cloud.firestore_v1 import SERVER_TIMESTAMP
cred = credentials.Certificate('./serviceAccountKey.json')
firebase_admin.initialize_app(cred)
db = firestore.client()
# 写入
doc_ref = db.collection('messages').document()
doc_ref.set({
'user': 'Bob',
'text': 'Hello from Python!',
'createdAt': SERVER_TIMESTAMP
})
print(f"消息 ID: {doc_ref.id}")
# 查询
docs = db.collection('messages').order_by('createdAt', direction='DESCENDING').limit(10).stream()
for doc in docs:
print(f"{doc.to_dict()['user']}: {doc.to_dict()['text']}")
# 实时监听(需长时间运行的脚本)
def on_snapshot(col_snapshot, changes, read_time):
for change in changes:
if change.type.name == 'ADDED':
print(f"[实时] {change.document.to_dict()['user']}: {change.document.to_dict()['text']}")
col_ref = db.collection('messages')
watch = col_ref.on_snapshot(on_snapshot)
# watch.unsubscribe() # 停止监听
预期输出
消息 ID: abc123...
[新消息] Alice: 大家好!
[新消息] Bob: Hello from Python!
// 实时监听:当有新消息时自动打印
[实时] Carol: 我也在!
关键点
onSnapshot是 Firestore 的核心:变更实时推送serverTimestamp()避免客户端时间不同步docChanges()区分 add/modify/remove 事件- 离线数据由 SDK 自动处理,联网后同步
- 免费层足够学习使用(每日 5 万读 / 2 万写)