Firebase身份验证状态更改后初始化Vue应用

时间:2017-07-20 02:53:01

标签: firebase vue.js firebase-authentication vuejs2 vuex

我的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应用程序?如果我做错了什么,请告诉我。

尝试

  1. 我试过"unsubscribe" approach;然而,它的问题是onAuthStateChanged只会被触发一次。因此,如果我将关键this.$store.commit('syncAuthUser', user)放在那里,那么该代码只会在创建应用程序时执行,而不是在用户注销时执行。
  2. 我也尝试过更好的方法here。我确实为我工作过,但是我感到很难将onAuthStateChanged包裹在另一个Promise中,因为肯定必须有更好的方法;另外,将auth用户初始化登录放在路径文件中似乎是错误的。

2 个答案:

答案 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()

TL; DR

使用Firebase onAuthStateChanged时,您必须实施ObservableSubscriptions个对象。 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为真为止。