При интеграции Firebase Auth в мое приложение Vue я недавно натолкнулся на проблему, когда вошедший в систему пользователь при обновлении страницы не будет распознаваться как вошедший в систему (и, следовательно, перенаправляется на страницу входа при попытке доступа к защищенному маршруту).
Это связано с тем, что исходный обратный вызов firebase.auth().onAuthStateChanged(...)
не был возвращен до того, как охранник маршрута оценил состояние.
Существует несколько предложенных решений, но ни одно из которых не показалось мне чистым.Наиболее заметным, по-видимому, является задержка фактического монтирования приложения путем перемещения new Vue(...).$mount('#app')
внутри обратного вызова onAuthStateChanged()
.
Так как в большинстве случаев первоначальное представление не требует входа пользователя в систему (или может бытьСам путь входа в систему) не должно быть необходимости откладывать монтирование всего приложения до тех пор, пока не вернется обратный вызов firebase.
Я думаю, что мог бы найти лучшее решение, но был бы признателен за некоторую обратную связь, если вы сможетепроблема с этим подходом или возможность его дальнейшего улучшения.
Примечание. Я использую глобальное хранилище состояний Vuex для управления состоянием приложения.
Я инициализирую обратный вызов firebase onAuthStateChanged()
в main.js
в хуке жизненного цикла created
компонента App:
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()
}
})
Есть ли проблемы, о которых я не знаюс таким подходом или его можно улучшить?