Vue应用中注册流程时序与onAuthStateChanged触发致权限问题
解决Vue+Firebase注册时用户文档未创建就拉取的权限问题
问题描述
我在Vue应用中要实现完整的用户注册流程:创建Firebase认证用户 → 创建对应的Firebase用户文档 → 从文档拉取信息填充应用状态。但当前流程存在时序问题:调用createUserWithEmailAndPassword()后,onAuthStateChanged()会立即触发,在我创建用户文档之前就尝试拉取文档,直接抛出权限错误。
核心疑问:在userAuth.vue中,执行完await store.signUp(...)后还没来得及创建用户文档,onAuthStateChanged就触发并执行拉取逻辑,是不是因为handleAuth()里的Promise异步特性导致的?
相关代码片段
userAuth.vue
await store.signUp(email.value, password.value); const docRef = doc(db, "users", store.userId); await setDoc(docRef, { // 用户数据 })
Pinia Store(general.js)
async signUp(email, password) { try { const res = await createUserWithEmailAndPassword(auth, email, password); this.user = res.user; this.userId = res.user.uid; } catch (err) { console.log(err); await this.signOut(); throw new Error("could not complete signup"); } }, async handleAuth() { const auth = getAuth(); return new Promise((resolve) => { auth.onAuthStateChanged(async (user) => { if (user) { await this.setAuthIsReady(true); let info = await this.getUserInfo(user.uid); await this.setUser({ // 用户文档数据 }); resolve(); } else if (!user) { await this.clearUserData(); await this.setAuthIsReady(false); resolve(); } }); }); },
路由配置(index.js)
Router.beforeEach(async (to, from, next) => { const authStore = useGenStore(); if (authStore.isAuthenticated != true) { await authStore.handleAuth(); }
解决方案
方法1:将创建文档逻辑移到Store的注册流程中
修改signUp方法,确保创建认证用户后立即生成对应的Firebase文档,再更新Store状态。这样onAuthStateChanged触发时,文档已经存在:
async signUp(email, password) { try { const res = await createUserWithEmailAndPassword(auth, email, password); const userId = res.user.uid; // 先创建用户文档 const docRef = doc(db, "users", userId); await setDoc(docRef, { email: email, createdAt: serverTimestamp() // 其他用户初始数据 }); // 再更新Store状态 this.user = res.user; this.userId = userId; } catch (err) { console.log(err); await this.signOut(); throw new Error("could not complete signup"); } },
方法2:在拉取文档时处理不存在的情况
如果不想调整注册流程,可以在getUserInfo方法中判断文档是否存在,返回默认值或添加重试逻辑:
async getUserInfo(userId) { const docRef = doc(db, "users", userId); const docSnap = await getDoc(docRef); if (!docSnap.exists()) { // 返回默认空结构 return {}; // 或者添加短暂重试逻辑(适合网络延迟场景) // await new Promise(resolve => setTimeout(resolve, 500)); // return this.getUserInfo(userId); } return docSnap.data(); }
方法3:临时关闭注册时的认证监听
注册前移除onAuthStateChanged监听,等文档创建完成后重新监听(需管理监听的 unsubscribe 函数):
- 在Store中添加监听管理方法:
async handleAuth() { const auth = getAuth(); return new Promise((resolve) => { // 保存unsubscribe函数 this.authUnsubscribe = auth.onAuthStateChanged(async (user) => { // 原有逻辑... }); }); }, // 添加移除监听的方法 unsubscribeAuth() { if (this.authUnsubscribe) { this.authUnsubscribe(); this.authUnsubscribe = null; } }
- 在
userAuth.vue的注册流程中使用:
// 注册前先移除监听 store.unsubscribeAuth(); try { await store.signUp(email.value, password.value); const docRef = doc(db, "users", store.userId); await setDoc(docRef, { // 用户数据 }); } finally { // 注册完成后重新启动监听 await store.handleAuth(); }
内容的提问来源于stack exchange,提问作者jdost_26
相关产品推荐
相关产品推荐

