Firebase 实时数据库:无后端数据同步

FreeGuideOnline 最新 2026-06-13

Firebase 实时数据库:无后端数据同步

什么是 Firebase 实时数据库?

Firebase 实时数据库是一种云托管的 NoSQL 数据库,数据以 JSON 格式存储,并在所有连接的客户端之间实时同步。它最大的特点就是让你无需编写任何后端代码,就能实现数据在用户间的即时共享。

  • 无后端:不需要搭建自己的服务器或编写 API,所有数据存取与权限控制都通过前端 SDK 配置。
  • 实时同步:当数据库中数据发生变化时,所有订阅该数据的客户端会在毫秒级收到更新。
  • 离线支持:数据会被缓存到本地,即使网络断开也能正常读写,等网络恢复后自动同步。
  • 从原型到产品:可扩展至数百万并发连接,适合快速开发和规模增长。

本教程面向完全初学者,将带你从零搭建一个实时同步的应用示例。你将学会读写数据、监听变化、设计数据结构以及编写安全规则。

核心概念

在动手之前,先了解几个关键概念。

数据是一棵 JSON 树

整个数据库就是一棵巨大的 JSON 对象。没有表、没有集合,所有数据都存在于嵌套的键值对中。

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "email": "ada@example.com"
    },
    "ghopper": {
      "name": "Grace Hopper",
      "email": "grace@example.com"
    }
  },
  "chat": {
    "room1": {
      "message1": {
        "text": "Hello!",
        "timestamp": 1620000000
      },
      "message2": { ... }
    }
  }
}

引用(Reference)与路径

通过路径来定位数据中的某个节点。例如 users/alovelace 指向 Ada 的整个对象。在代码中,我们创建该路径的引用,随后所有操作都基于这个引用。

实时监听与一次性读取

  • once():读取一次数据,不监听后续变化。
  • on():持续监听某个路径,当数据变化时自动触发回调。
  • 实时数据库的 on() 是核心武器,它推送增量更新,无需轮询。

环境搭建

1. 创建 Firebase 项目

  1. 前往 Firebase 控制台 并登录。
  2. 点击“添加项目”,按提示命名(例如 realtime-tutorial)。
  3. 可选开启 Google Analytics,然后创建项目。
  4. 在项目左侧菜单中,点击“构建” → “实时数据库”。
  5. 点击“创建数据库”,选择服务器位置(一般默认),以测试模式启动(方便初期开发,稍后会设置安全规则)。

2. 注册 Web 应用并获取配置

  1. 在项目概览页点击“添加应用”选择 Web(</>)。
  2. 输入应用昵称(如 Web App),注册应用。
  3. 你将看到一段 Firebase 配置代码,复制它。形如:
const firebaseConfig = {
  apiKey: "AIzaSy...",
  authDomain: "...",
  databaseURL: "https://your-project-id.firebaseio.com",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "..."
};

3. 在 HTML 中引入 Firebase SDK

创建一个 index.html 文件,使用 CDN 引入 Firebase SDK(这里使用模块化版本,兼容现代浏览器):

<!DOCTYPE html>
<html>
<body>
  <script type="module">
    import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js";
    import { getDatabase, ref, set, push, onValue, get } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-database.js";

    const firebaseConfig = {
      // 粘贴你刚才复制的配置
    };

    const app = initializeApp(firebaseConfig);
    const db = getDatabase(app);
    
    // 所有数据库操作代码将写在这里
  </script>
</body>
</html>

现在你可以开始操作实时数据库了。

写入数据

数据库提供多种写入方法,适用于不同场景。

使用 set() 覆盖写入

set() 会将数据写入指定路径,覆盖原有内容。适合用于保存用户资料等完整对象。

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

function writeUserData(userId, name, email) {
  const userRef = ref(db, 'users/' + userId);
  set(userRef, {
    username: name,
    email: email
  });
}

// 调用
writeUserData("alovelace", "Ada Lovelace", "ada@example.com");

此时数据库会新增 users/alovelace 节点,包含 usernameemail 字段。

使用 push() 自动生成唯一键

当你需要添加一条“列表”数据(如聊天消息、任务项)时,希望拥有唯一 ID 且不覆盖之前的内容,就可以使用 push()。它会生成一个基于时间戳的唯一键。

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

function addChatMessage(roomId, text) {
  const messagesRef = ref(db, 'chat/' + roomId);
  const newMessageRef = push(messagesRef);
  set(newMessageRef, {
    text: text,
    timestamp: Date.now()
  });
}

addChatMessage("room1", "Hello everyone!");

数据库中 chat/room1/ 下会多出一个类似 -NxJg... 的键,其值就是你设置的对象。

使用 update() 更新部分字段

若只更新一个对象的部分字段而不覆盖整个节点,用 update()

import { update, ref } from "firebase/database";

const updates = {};
updates['users/alovelace/email'] = "ada.lovelace@newdomain.com";
updates['users/ghopper/email'] = "grace@newdomain.com";
update(ref(db), updates);

可以同时原子性地更新多个路径。

读取与监听实时更新

这里是体现“无后端实时同步”的关键。

一次性读取 get()

使用 get() 获取路径数据的快照,不会接收后续变化。

import { get, ref } from "firebase/database";

const userRef = ref(db, 'users/alovelace');
get(userRef).then((snapshot) => {
  if (snapshot.exists()) {
    console.log(snapshot.val()); // { username: "Ada Lovelace", email: "..." }
  } else {
    console.log("No data available");
  }
});

实时监听 onValue()

使用 onValue() 可以持续监听某个路径。初始时获取一次全部内容,之后每当该路径下任何数据发生变化,都会再次触发回调并直接收到当前该路径下的所有数据。

import { onValue, ref } from "firebase/database";

const messagesRef = ref(db, 'chat/room1');
onValue(messagesRef, (snapshot) => {
  const data = snapshot.val();
  if (data) {
    // 将消息列表渲染到页面
    console.log("New message list:", data);
  }
});

onValue 每次触发都返回该路径下的完整对象。对于大型列表,可考虑后面介绍的更精细监听。

监听子节点变化:onChildAdded / onChildChanged / onChildRemoved

当数据库列表很大时,监听整个列表的 onValue 会重传所有数据。使用 child 系列事件 可以让你的应用只处理增量变化,更高效地构建 UI。

import { onChildAdded, ref } from "firebase/database";

const messagesRef = ref(db, 'chat/room1');
onChildAdded(messagesRef, (data) => {
  // 每一条消息被添加时触发一次,data.key 和 data.val() 可用
  console.log("New message added:", data.key, data.val());
});

类似的还有 onChildChangedonChildRemoved。这些是构建聊天界面时的最佳实践。

设计高效的数据结构

实时数据库是 JSON 树,扁平化是关键原则。避免深层嵌套导致读取整个树,应尽量将数据分散到不同路径,在客户端通过多路径监听来组装。

错误示例:嵌套过深

{
  "chats": {
    "room1": {
      "name": "General",
      "messages": {
        "msg1": { "text": "Hi", "sender": { "name": "Ada", "avatar": "url" } },
        ...
      }
    }
  }
}

这样每次获取房间消息时都会把发送者的完整个人信息下载下来,且不易复用。

推荐结构:数据扁平化

{
  "users": {
    "alovelace": { "name": "Ada", "avatar": "ada.jpg" },
    "ghopper": { "name": "Grace", "avatar": "grace.jpg" }
  },
  "rooms": {
    "room1": { "name": "General", "lastMessage": "Hi" }
  },
  "room-messages": {
    "room1": {
      "msg1": {
        "senderId": "alovelace",
        "text": "Hi",
        "timestamp": 1620000000
      }
    }
  }
}

获取消息列表时只下载轻量的 room-messages,然后根据 senderId 再去 users 路径下获取一次用户信息(可以结合一次性读取或单独监听)。

避免使用数组

数据库存储的数组实际上会转换成对象(键为数字索引),当删除或插入元素时索引会混乱。始终使用 push() 生成的唯一键作为列表项标识。

安全性规则入门

测试模式允许所有人读写,千万不能在生产环境中使用。实时数据库通过声明式的 JSON 规则来控制访问。

规则基本结构

在 Firebase 控制台的“实时数据库” → “规则”选项卡中编辑。规则使用类似 JavaScript 的表达式,但运行在服务器端。

{
  "rules": {
    ".read": "auth != null",  // 只有登录用户可读
    ".write": "auth != null",
    "users": {
      "$userId": {
        ".write": "$userId === auth.uid" // 用户只能写自己的数据
      }
    }
  }
}
  • auth:当前认证用户的信息,如果未登录则为 null
  • $variable:路径中的通配符,可在条件中使用。
  • 规则从上往下寻找,子节点可以继承或覆盖父节点的权限。

常用规则示例

公开可读,仅认证可写

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

用户房间消息规则(结合数据库结构):

{
  "rules": {
    "room-messages": {
      "$roomId": {
        ".read": "root.child('members/'+$roomId+'/'+auth.uid).exists()",
        ".write": "root.child('members/'+$roomId+'/'+auth.uid).exists()"
      }
    },
    "members": {
      // ...
    }
  }
}

规则里面可使用 root 引用数据库根节点,实现跨路径校验。

部署规则后,所有未授权的读写都会被拒绝,实时数据库会自动报错,你可以在客户端捕获错误。

完整示例:实时聊天应用

我们将之前的概念整合到一个简单的匿名聊天室中。假设我们已启用匿名身份验证(Firebase Auth 中的匿名登录),并设置好了安全规则。

HTML 结构(简化):

<div id="messages"></div>
<input id="messageInput" placeholder="Type a message...">
<button id="sendButton">Send</button>

JavaScript 核心代码

import { getDatabase, ref, push, set, onChildAdded } from "firebase/database";
// 初始化省略,参照前面环境搭建

const db = getDatabase();
const roomId = "public";   // 公用房间

// 发送消息
document.getElementById('sendButton').addEventListener('click', () => {
  const input = document.getElementById('messageInput');
  const text = input.value.trim();
  if (text === '') return;
  
  const messagesRef = ref(db, `room-messages/${roomId}`);
  const newMsgRef = push(messagesRef);
  set(newMsgRef, {
    text: text,
    timestamp: Date.now(),
    sender: "anonymous"   // 实际应使用 auth 信息
  });
  input.value = '';
});

// 实时监听新消息
const messagesRef = ref(db, `room-messages/${roomId}`);
onChildAdded(messagesRef, (data) => {
  const msg = data.val();
  const div = document.createElement('div');
  div.textContent = `[${new Date(msg.timestamp).toLocaleTimeString()}] ${msg.text}`;
  document.getElementById('messages').appendChild(div);
});

无需任何服务器代码,你的聊天应用就已经可以实时运行在所有打开的浏览器标签页中了。这就是 Firebase 实时数据库带来的“无后端数据同步”。

总结与下一步

你已经掌握了 Firebase 实时数据库的核心用法:

  • 以 JSON 树存储数据,用路径引用操作。
  • 通过 setpushupdate 写入数据。
  • 使用 onValueonChildAdded 等实时监听变化。
  • 设计扁平数据结构,避免深层嵌套和数组。
  • 配置安全规则保护数据。

从这里开始,你可以继续深入学习:

  • Firebase Authentication:集成登录,使安全规则中的 auth 有效。
  • 离线持久化:在网页端启用 enableIndexedDbPersistence 增强离线体验。
  • 查询与排序:使用 orderByChildlimitToLast 等对数据进行高级读取。
  • Cloud Functions:在数据库中数据变化时触发后端代码,扩展逻辑。

现在,打开你的代码编辑器,创建一个无需后端的数据驱动应用吧!