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

Vue3+Pinia+VueRouter下Firebase用户信息预加载优化咨询

优化Firebase Auth与Firestore请求避免重复调用

技术栈

vue@3.2.47
pinia@2.0.35
vue-router@4.1.6

问题现状

目前每次路由导航都会触发storeAuth.init(),导致重复请求Firebase Auth和Firestore,既浪费资源又影响性能。需要实现:

  • 应用启动时仅加载一次用户信息
  • 路由守卫等待用户信息加载完成后再执行
  • 两种场景(直接输入网址、路由导航)都能正常获取完整用户信息(依赖Firestore文档)

优化方案

1. 修改Auth Store,实现单次初始化+状态监听

核心思路:添加状态标记避免重复初始化,同时持续监听Auth状态变化,仅当用户UID变更时才请求Firestore数据。

修改storeAuth:

export const useStoreAuth = defineStore('storeAuth', () => {
  const user = ref<User | null>(null)
  const isInitialized = ref(false) // 标记是否已完成初始化
  const currentUid = ref<string | null>(null) // 跟踪当前用户UID,避免重复请求profile
  const profileStore = useProfileStore()

  // 初始化Auth监听(仅执行一次)
  const init = async () => {
    if (isInitialized.value) {
      return user.value // 已初始化直接返回用户数据
    }

    return new Promise((resolve) => {
      // 持续监听Auth状态变化,不要unsubscribe,否则登出/切换用户不会更新状态
      onAuthStateChanged(auth, async (firebaseUser) => {
        if (firebaseUser) {
          // 只有当UID变化时才重新请求Firestore
          if (firebaseUser.uid !== currentUid.value) {
            currentUid.value = firebaseUser.uid
            await profileStore.getUserInfo(firebaseUser.uid)
          }
          // 更新用户数据
          user.value = {
            id: firebaseUser.uid,
            email: firebaseUser.email,
            firstName: profileStore.firstName,
            lastName: profileStore.lastName,
            phone: profileStore.phone,
            streetAndNumber: profileStore.streetAndNumber,
            city: profileStore.city,
            postalCode: profileStore.postalCode,
            nationality: profileStore.nationality,
            gender: profileStore.gender
          }
        } else {
          user.value = null
          currentUid.value = null
          profileStore.resetProfile() // 登出时清空profile数据
        }
        isInitialized.value = true
        resolve(user.value)
      })
    })
  }

  // 其他方法...
  return { user, init, isInitialized }
})

2. 优化Profile Store,添加缓存与重置方法

给profileStore添加重置方法,同时可以通过本地缓存进一步减少Firestore请求:

export const useProfileStore = defineStore('profile', () => {
  const firstName = ref<string>('')
  const lastName = ref<string>('')
  const phone = ref<string>('')
  const dob = ref<string[]>([])
  const streetAndNumber = ref<string>('')
  const city = ref<string>('')
  const postalCode = ref<string>('')
  const nationality = ref<string>('')
  const gender = ref<string>('')

  async function getUserInfo(uid:string) {
    // 先检查本地缓存,存在则直接使用(可选)
    const cachedProfile = sessionStorage.getItem(`profile_${uid}`)
    if (cachedProfile) {
      const data = JSON.parse(cachedProfile)
      Object.assign({
        firstName, lastName, phone, dob,
        streetAndNumber, city, postalCode, nationality, gender
      }, data)
      return
    }

    const docRef = doc(db, "members", uid);
    const docSnap = await getDoc(docRef);
    if (docSnap.exists()) {
      const data = docSnap.data()
      firstName.value = data.firstName
      lastName.value = data.lastName
      phone.value = data.phone
      dob.value = data.dob
      streetAndNumber.value = data.streetAndNumber
      city.value = data.city
      postalCode.value = data.postalCode
      nationality.value = data.nationality
      gender.value = data.gender
      // 缓存到sessionStorage,有效期同会话
      sessionStorage.setItem(`profile_${uid}`, JSON.stringify(data))
    }
  }

  // 登出时清空profile数据
  function resetProfile() {
    firstName.value = ''
    lastName.value = ''
    phone.value = ''
    dob.value = []
    streetAndNumber.value = ''
    city.value = ''
    postalCode.value = ''
    nationality.value = ''
    gender.value = ''
    // 清除对应缓存
    const currentUid = useStoreAuth().currentUid
    if (currentUid.value) {
      sessionStorage.removeItem(`profile_${currentUid.value}`)
    }
  }

  return {
    firstName, lastName, phone, dob,
    streetAndNumber, city, postalCode, nationality, gender,
    getUserInfo, resetProfile
  }
})

3. 修改路由守卫,仅等待初始化完成一次

在应用启动时先完成Auth初始化,后续路由导航不再重复调用init():

// 先在路由文件外初始化一次Auth
const storeAuth = useStoreAuth()
// 应用启动时执行初始化,确保路由守卫执行前用户信息已加载
const authInitPromise = storeAuth.init()

router.beforeEach(async (to) => {
  // 等待初始化完成(仅第一次会等待,后续直接通过)
  await authInitPromise

  // 原有的路由逻辑
  if (
    storeAuth.user &&
    (to.name === 'signin' || to.name === 'register' || to.name === 'request-password-rest')
  ) {
    return { name: 'home' }
  } else {
    return
  }
})

效果说明

  • 应用启动(直接输入网址):仅执行一次Auth监听和Firestore请求
  • 路由导航:直接使用已缓存的用户数据,无额外请求
  • 用户登出/切换账号:自动触发新的Firestore请求(仅当UID变化时)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 18:05:43