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
相关产品推荐
相关产品推荐

