You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase添加聊天打字状态检测后原有消息收发功能失效

问题根因

你遇到的所有异常都是三个低级逻辑错误导致的,和Firebase本身稳定性无关:

  • 打字状态存储路径错误,直接覆盖了全部历史聊天记录
    你原本的聊天消息存储层级是messages/发送方/接收方/时间戳,所有历史消息都存在messages/发送方/接收方这个父节点下的时间戳子节点里。但你新增打字状态逻辑时,直接对这个父节点执行set()操作——Firebase Realtime Database的set()是全量覆盖写入,不是增量更新,你每次触发打字状态更新,就会把这个路径下之前存的所有聊天消息全部删掉,替换成只有tpg字段的对象。后续拉取消息时找不到原本的usr、msg字段,自然会显示undefined,历史消息也会全部丢失。
  • 打字状态监听缺少空值判断,直接触发JS报错打断逻辑
    页面刚加载时对方还没触发过打字动作,你监听的打字状态节点初始是空值,snapshot.val()会返回null,这时候你直接执行typing.tpg会直接抛出JS类型错误,打断后续所有页面逻辑,这也是消息有时加载不出来的原因之一。
  • 用户名非空校验逻辑位置不对,没有拦住空输入
    你用prompt()获取昵称和聊天对象时,如果用户点击弹窗的取消按钮,返回值就是null,如果校验逻辑没写在所有数据库操作的最前面提前终止代码运行,就会往数据库里写入messages/null/null这种异常节点,生成你看到的null用户发消息的脏数据。
    另外你原代码里"yes"、"no"的闭合引号用了中文全角符号,也会导致JS解析异常。
修复步骤
  1. 把打字状态数据挪到独立的typing_status根节点存储,和聊天消息的messages节点完全隔离,从根源上避免覆盖聊天记录
  2. 在代码最开头添加严格的非空校验,只要用户名、接收方名为空或者用户点了取消,直接终止后续逻辑
  3. 所有读取数据库快照的位置,先判断快照值是否存在,再读取内部字段,避免空值报错
  4. 渲染聊天消息时加字段校验,只有存在usr和msg字段的节点才当成消息渲染,过滤异常脏数据
修复后可直接运行的完整代码
firebase.initializeApp(firebaseConfig);
const db = firebase.database();

// 最开头做非空校验,拦住空输入
const username = prompt("Nickname:");
const receiver = prompt("Receiver's name:");
if (!username || !receiver || !username.trim() || !receiver.trim()) {
  alert("昵称和聊天对象名称不能为空");
  throw new Error("用户名或接收方名为空,终止运行");
}

// 发送消息逻辑
document.getElementById("send-message").addEventListener("submit", postChat);
function postChat(e) {
  e.preventDefault();
  const timestamp = Date.now();
  const chatTxt = document.getElementById("chat-txt");
  const message = chatTxt.value.trim();
  if (!message) return; // 空消息不发送
  chatTxt.value = "";

  // 双向写入消息记录
  db.ref("messages/" + username + "/" + receiver + "/" + timestamp).set({
    usr: username,
    msg: message,
  });
  db.ref("messages/" + receiver + "/" + username + "/" + timestamp).set({
    usr: username,
    msg: message,
  });
  sendTyping(false);
}

// 拉取并渲染消息
const fetchChat = db.ref("messages/" + username + "/" + receiver + "/");
fetchChat.on("child_added", function (snapshot) {
  const messages = snapshot.val();
  // 加字段校验,只有是正常消息才渲染,过滤异常数据
  if (!messages || !messages.usr || !messages.msg) return;
  const msg = "<li>" + messages.usr + " : " + messages.msg + "</li>";
  document.getElementById("messages").innerHTML += msg;
});

// 打字状态逻辑,单独存在typing_status节点,不和消息混放
function sendTyping(isTyping) {
  // 路径改成独立的typing_status节点,不会覆盖消息
  db.ref("typing_status/" + username + "/" + receiver).set({
    tpg: isTyping ? "yes" : "no" // 全部使用英文半角引号
  });
}

var searchTimeout;
document.getElementById("chat-txt").onkeydown = function() {
  if (searchTimeout != undefined) clearTimeout(searchTimeout);
  searchTimeout = setTimeout(() => sendTyping(false), 1500);
  sendTyping(true);
}

// 监听对方打字状态
const areTheyTyping = db.ref("typing_status/" + receiver + "/" + username);
areTheyTyping.on("value", function(snapshot) {
  const typingData = snapshot.val();
  // 先判空再取字段,避免报错
  const status = typingData?.tpg;
  if (status == "yes") {
    document.getElementById("writing").innerHTML = "typing...";
  } else {
    document.getElementById("writing").innerHTML = "";
  }
});
额外注意

你之前已经写入数据库的null节点、被覆盖的坏数据,可以直接去Firebase控制台手动删掉,不然会一直被拉取到前端显示异常。

内容的提问来源于stack exchange,提问作者pmisrfvfpimm23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:03:24