【问题标题】:Initialize Vue app after Firebase auth state changedFirebase 身份验证状态更改后初始化 Vue 应用程序
【发布时间】:2017-12-25 12:51:17
【问题描述】:

我在我的 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()
  }
})

问题

正如 cmets 中所提到的,应用程序获取经过身份验证的用户为时已晚。所以,当你在登录时去/home时,应用程序会在路由中运行beforeEachcheck ...并且用户实例暂时不可用,所以它会认为你没有登录, 事实上你是(从我的计算属性中可以清楚地看出,稍后会正确更新)。

问题

那么,我该如何更改我的代码,以便仅在 Firebase 返回用户并且将该用户传递到 Vuex 商店之后才初始化 Vue 应用程序?如果我做错了什么,请告诉我。

尝试

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

【问题讨论】:

  • 嗨,我在使用 Firebase 时也遇到了这个问题。我使用 Angular,但遇到了同样的问题。最后,我认为可靠的方法是在组件中等待用户身份验证。是的,observables 是通往这里的道路。

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


【解决方案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 为真之前,您可能完全阻止路由加载。

【讨论】:

  • 如果您的应用确实有会员区和非会员区,但共享组件怎么办。如果未登录,它将无休止地返回 null 并且永远不会加载应用程序。未登录和等待登录成功响应没有区别。
  • 不确定你的意思,如果你没有登录,onAuthStateChanged 仍然会被调用,user 设置为 null,所以你的应用程序仍然会加载,但你的商店状态说用户未通过身份验证?
  • null 将始终是可观察对象的初始发出值,在您登录时也是如此。可观察对象在更改状态时发出一个新值,但到那时您的应用程序已经可以挂载或不。希望我把我的问题说清楚了。
  • 好吧,我只是想知道。我发现很难为某些组件实现高级身份验证状态。例如:当一个组件完成加载时,我运行了一个函数,但我发现自己遇到了时间问题。有时它确实获得了用户对象,有时它只是返回 null 所以我不得不在组件中等待用户对象。这是一个 Angular 应用程序,但我很难在更高层次上获得抽象解决方案,比如在服务或其他东西中。也许有了这个输入,如果我找到更好的解决方案,我可以看看。不过现在我想起来可能是 Angular 的东西。
  • 是的,只需做一个小实验,将firebase.auth().onAuthStateChanged 放在 angular 或其他任何东西之外,看看回调与 console.log 有什么关系。我在多个应用程序中都以这种方式工作
【解决方案2】:

示例

./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 的身份验证响应。

查看第一个参数,nextOrObserver 来自documentation

onAuthStateChanged(nextOrObserver, error, completed) returns function()

可观察的设计模式(即异步、推送、多播)记录在here

【讨论】:

  • 尽量避免答案中的链接过多 - 如果它们死了,那么你的答案就会变得不清楚。
  • @jmwatts 好点,将额外链接移至 cmets:在 Vue 中使用 Observables 的两个很好的例子:Vue w/RxJSVue w/vue-rx
猜你喜欢
  • 1970-01-01
  • 2020-07-13
  • 2021-08-02
  • 2015-01-16
  • 1970-01-01
  • 2022-01-04
  • 2021-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多