在将Firebase Auth集成到我的Vue应用程序中时,我最近偶然发现了一个问题,其中页面刷新上的登录用户将不会被识别为已登录(因此在尝试访问受保护的路由时会重定向到登录页面)。
这是由于以下事实:在路由守卫评估状态之前,firebase.auth().onAuthStateChanged(...)
的初始回调尚未返回。
有几种建议的解决方案,但对我来说似乎都不干净。最突出的似乎是通过在new Vue(...).$mount('#app')
回调内移动onAuthStateChanged()
来延迟应用程序的实际安装。
由于在大多数情况下,初始视图不需要用户登录(或者可能是Login路由本身),因此无需将整个应用的安装延迟到firebase回调返回之前。
我想我可能已经找到了一个更好的解决方案,但是如果您发现此方法有任何问题或者可以进一步改进,我将不胜感激。
注意:我使用Vuex全局状态存储来管理应用程序状态。
我在App组件的onAuthStateChanged()
生命周期挂钩中的main.js
中初始化firebase 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的初始化状态,默认为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()
}
})
这种方法是否有我不知道的问题,或者可以进一步改进吗?
答案 0 :(得分:0)
只需在您的Router.js文件中添加
router.beforeEach((to,from,next) => {
if(to.matched.some(record => record.meta.auth)){
firebase.auth().onAuthStateChanged((user) => {
if(!user)
{
next({
path: '/'
})
} else {
next()
}
})}else next()
})
这既可以防止页面访问,也可以在用户未经授权时将其赶出该页面。我自己在自己的一个网站上使用了它,到目前为止,还没有问题