在将Firebase Auth集成到我的Vue应用程序中时,我最近偶然发现了一个问题,页面刷新时登录的用户将不会被识别为已登录(因此在尝试访问受保护的路由时会重定向到登录页面)。
这是因为
firebase.auth().onAuthStateChanged(...)
在路由守卫评估状态之前未返回。
有几个提议的解决方案,但在我看来没有一个是干净的。最突出的问题似乎是通过移动来延迟应用程序的实际安装
new Vue(...).$mount('#app')
内部
onAuthStateChanged()
因为在大多数情况下,初始视图不需要用户登录(或者可能是登录路径本身),所以不需要延迟整个应用程序的安装,直到firebase回调返回。
我想我可能已经找到了一个更好的解决方案,但如果您能看到这种方法的任何问题,或者如果可以进一步改进,我将非常感谢您的反馈。
注意:我使用Vuex全局状态存储来管理应用程序状态。
我初始化firebase
onAuthStateChanged()
回拨
main.js
在
created
应用程序组件的生命周期挂钩:
new Vue({
router,
store,
render: h => h(App),
created() {
firebaseApp.auth().onAuthStateChanged((user) => {
if(!this.$store.getters.firebaseInitialized)
this.$store.commit('firebaseInitialized')
if (user) {
if(!this.$store.authenticated || this.$store.getters.loggedInUser.uid != user.uid)
this.$store.dispatch('logIn', user)
}
})
}
}).$mount('#app')
这个
firebaseInitialized
状态中的布尔值跟踪firebase的init状态,即
false
默认设置为
true
在那之前
onAuthStateChanged
回拨。
内
router.js
我现在创建了一个“投票承诺”,上面说
firebaseInitialized的
并等待初始化完成(或超时),然后再检查用户的身份验证状态:
function ensureFirebaseIsInitialized() {
let timeout = 5000;
let start = Date.now();
return new Promise(function (resolve, reject) {
(function waitForFirebaseInit(){
if (store.getters.firebaseInitialized)
return resolve();
else if((Date.now() - start) >= timeout)
return reject(new Error('Firebase initialize timeout'))
setTimeout(waitForFirebaseInit, 30);
})();
});
}
这个承诺现在可以在
router.beforeEach
:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
ensureFirebaseIsInitialized().then(() => {
if(store.getters.authenticated) {
next()
}
else {
next('/login')
}
}).catch((error) => {
console.log('firebase not initialized')
next('/login')
})
}
else {
next()
}
})
这种方法是否有我不知道的问题,或者可以进一步改进?