我的Vue / vuex / vue-router + Firebase应用程序中有一点难题。引导代码应该初始化Firebase,以及Vuex中的登录用户(如果有):
new Vue({
el: '#app', router, store, template: '<App/>', components: { App },
beforeCreate() {
firebase.initializeApp(firebaseConfig)
// Because the code below is async, the app gets created before user is returned...
.auth().onAuthStateChanged(user => {
this.$store.commit('syncAuthUser', user) // this executes too late...
})
}
})
在我的路线中,我有
{ path: '/home', name: 'Home', component: Home, meta: { requiresAuth: true } }
以及
router.beforeEach((to, from, next) => {
let authUser = store.getters.authUser // this is not set just yet...
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!authUser) {
next('signin') // /home will always hit here...
}
next()
}
else {
next()
}
})
正如评论中所提到的,该应用程序得到了经过身份验证的用户太晚了。因此,当您在登录时转到/home
时,应用程序将在路由中运行beforeEach
检查...并且用户实例尚未可用,因此它会认为您是没有登录,实际上你是(从我的计算属性中可以清楚地看到,稍后会对其进行适当更新)。
那么,如何更改我的代码,以便仅在Firebase返回用户之后以及在该用户传递到Vuex商店之后才初始化Vue应用程序?如果我做错了什么,请告诉我。
onAuthStateChanged
只会被触发一次。因此,如果我将关键this.$store.commit('syncAuthUser', user)
放在那里,那么该代码只会在创建应用程序时执行,而不是在用户注销时执行。onAuthStateChanged
包裹在另一个Promise
中,因为肯定必须有更好的方法;另外,将auth用户初始化登录放在路径文件中似乎是错误的。答案 0 :(得分:1)
./ App.vue
created: function() {
auth.onAuthStateChanged(user => {
this.$store.dispatch("SET_USER", user)
user.getIdToken().then( token => {
this.$store.dispatch("SET_TOKEN", token)
})
}
./ store.js(突变)
export default {
SET_USER(state, user) {
state.authenticated = !!user
state.user = {
email: user ? user.email : null,
displayName: user ? user.displayName : null,
photoURL: user ? user.photoURL : null,
}
}
SET_TOKEN(state, payload) {
state.token = payload
}
}
<强>生命周期强>
此示例使用Vue的created:
生命周期钩子。在created:
中,我们使用auth.onAuthStateChanged
来启动事件侦听器。您的应用现在正在侦听来自firebase的身份验证更改。
auth.onAuthStateChanged(user => { ...listening for user objects... })
只要Firebase注意到身份验证状态更改,就会返回user
对象。使用其属性和方法进行身份验证和授权。
财产: user.emailVerfied
方法: user.getIdToken()
使用Firebase onAuthStateChanged
时,您必须实施Observable
和Subscriptions
个对象。 RxJS Observable与promise
一样是异步的,它可以帮助您等待来获取Firebase的auth响应。
查看documentation中的第一个参数nextOrObserver
:
onAuthStateChanged(nextOrObserver, error, completed) returns function()
Observable 设计模式(即异步,推送,多播)记录在案here。
答案 1 :(得分:1)
最简单的方法是在挂载应用之前等待auth状态:
const app = new Vue({
router,
store,
render: h => h(App),
});
firebase.auth().onAuthStateChanged(user => {
store.commit('authStateChanged', user);
app.$mount('#app');
});
如果要在挂载之前显示微调器,请将其放在#app
中:
<div id="app"><div class="loader">Loading...</div></div>
实际上,如果您将身份验证状态逻辑提取到如上所述的authStateChanged
提交中,并且在这里也处理路由重定向,它将使您的操作更干净,应用程序更可靠。因为这将处理您在路线上时身份验证状态是否发生更改,而router.beforeEach
会检查您何时进入。
firebase.auth().onAuthStateChanged(user => {
store.commit('auth/authStateChanged', user);
const isAuthed = Boolean(user);
const { meta } = router.history.current;
if (isAuthed && meta.isPublicOnly) { // e.g. /login + /signup
router.push('/dashboard');
} else if (!isAuthed && meta.isPrivate) {
router.push('/login');
}
app.$mount('#app');
});
请注意,第一个router.beforeEach
回调发生在此之前,因此您应该忽略它,直到确定了身份验证状态为止。由于我们在onAuthStateChanged
中处理了第一次重定向,所以没关系。
state: {
isInited: false,
isAuthed: false,
},
mutations: {
authStateChanged(state, user) {
state.isAuthed = Boolean(user);
state.isInited = true;
}
}
router.beforeEach((to, from, next) => {
const { isInited, isAuthed } = store.state.user;
if (isInited) {
if (!isAuthed && to.matched.some(record => record.meta.isPrivate)) {
return next('/login');
} else if (isAuthed && to.matched.some(record => record.meta.isPublicOnly)) {
return next('/dashboard');
}
}
next();
});
您可能可以完全阻止路由加载,直到isInited
为真为止。