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

Vanilla JS项目登录登出无刷新时Firestore数据重复拉取问题

问题根因

  • auth.onAuthStateChanged 监听器的注册逻辑被放在了 signInWithGoogle 的登录成功回调中,每完成一次登录,就会新增一个独立的监听器实例。这类 Firebase 监听器不会随用户登出自动销毁,后续每次登录状态变更,所有已注册的监听器都会同时触发,因此登录多少次,fetchBooks 就会被调用多少次。
  • 额外隐患:当前 fetchBooks 拿到数据后直接调用 addCardToHTML 插入节点,没有清空已渲染的旧卡片,即使监听器问题修复,也可能出现卡片重复渲染的情况。

修复方案

方案1(推荐):全局仅注册一次鉴权监听器

将监听器逻辑从登录回调中移出,页面初始化时仅注册一次即可,修改后代码如下:

// 页面初始化阶段执行,仅注册一次鉴权状态监听
auth.onAuthStateChanged(user => {
  if (user) {
    // 拉取数据前先清空现有卡片,避免重复渲染
    clearExistingBookCards()
    fetchBooks()
  }
})

function signInWithGoogle(){
  const provider = new firebase.auth.GoogleAuthProvider()
  auth.signInWithPopup(provider)
    .then(result => {
      createNewUserDocument(result.user)
      // 移除此处重复的onAuthStateChanged注册逻辑
    }).catch(err => {
      console.log(err)
    })
  signUpForm.reset()
  signUpModal.hide()
  signInForm.reset()
  signInModal.hide()
}

// 新增清空已渲染图书卡片的方法
function clearExistingBookCards() {
  // 假设所有图书卡片都加了统一类名book-card,根据实际业务替换选择器即可
  document.querySelectorAll('.book-card').forEach(card => card.remove())
}

function fetchBooks() {
  const docRef = db.collection('users').doc(auth.currentUser.uid).collection('books')
  docRef.get().then(querySnapshot =>{
    console.log(querySnapshot)
    querySnapshot.forEach(doc => {
      const data = doc.data()
      addCardToHTML(data.title, data.author, data.pages, data.description, data.read)
    })
  })
}

方案2(适配特殊业务场景):注册新监听器前销毁旧实例

如果你的业务逻辑确实需要在登录后才注册监听器,需要先保存上一次监听器的取消函数,注册新监听器前先销毁旧实例:

// 全局变量保存监听器取消函数
let unsubscribeAuthListener = null

function signInWithGoogle(){
  const provider = new firebase.auth.GoogleAuthProvider()
  auth.signInWithPopup(provider)
    .then(result => {
      createNewUserDocument(result.user)
      // 销毁之前注册的旧监听器
      if (unsubscribeAuthListener) unsubscribeAuthListener()
      // 注册新监听器,onAuthStateChanged返回值就是取消该监听的方法
      unsubscribeAuthListener = auth.onAuthStateChanged(user => {
        if (user) {
          clearExistingBookCards()
          fetchBooks()
        }
      })
    }).catch(err => {
      console.log(err)
    })
  signUpForm.reset()
  signUpModal.hide()
  signInForm.reset()
  signInModal.hide()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:06:04