如何使Vue Router Guards等待Vuex?

时间:2018-12-19 11:47:27

标签: vue.js vuex vue-router

因此,我公开找到的所有问题答案都没有太大帮助,尽管它们“起作用”,但它们确实令人难以置信。

基本上我有一个vuex变量appLoading,最初它是true,但是一旦完成所有异步操作,它就会设置为false。我还有另一个名为user的vuex变量,它包含用户信息,这些信息一旦返回就将从异步操作中分派。

然后我还有一个路由器守卫来检查;

router.beforeEach((to, from, next) => {
  if (to.matched.some(route => route.meta.requiresAuth)) {
    if (store.getters.getUser) {
      return next();
    }
    return router.push({ name: 'index.signup' });
  }

  return next();
});

然后在我最初的Vue实例中显示加载状态,直到appLoading = false;

现在这个“有效”,但是确实有一个困扰我的问题。如果您加载该页面,则会看到应该看到的相反页面的“闪烁”。

因此,如果您已登录,则在首次加载时会看到注册页面的闪烁。如果您尚未登录,则将看到已登录页面的闪烁。

这很烦人,我将问题缩小到我的身份验证卫士。 似乎由于user不存在而将注册页面推送到路由器,然后由于user被提交而立即推送到登录页面。

我该如何以一种不令人讨厌的方式解决此问题,因为这有点烦人,令人沮丧的是,Vue甚至没有一点点官方文档/示例来解决如此普遍的问题,尤其是因为如此众多的Web应用程序都使用身份验证。

希望有人可以提供帮助。 :)

2 个答案:

答案 0 :(得分:1)

根据评论中的讨论:

最适合您的情况是将appLoading变量设为promise。这样一来,您就可以执行操作或等待操作,直到解决您的应用程序数据为止。

考虑appLoading用API调用promise初始化的promise,路由器钩子将类似于:

router.beforeEach((to, from, next) => {
  appLoading.then(() => {
    if (to.matched.some(route => route.meta.requiresAuth)) {
      if (store.getters.getUser) {
        return next();
      }
      return router.push({ name: "index.signup" });
    }

    return next();
  });
});

您可能只想将其保留为init代码的导出内容,而不是将其保留在Vuex中。 Vuex用于在组件之间共享的反应性数据。

答案 1 :(得分:0)

路由器 beforeEach hook 可以是一个 promise 并等待 Vuex 操作完成。类似的东西:

router.beforeEach(async (to, from, next) => {
  if (to.matched.some(route => route.meta.requiresAuth)) {
    await store.dispatch('init');

    if (store.getters.getUser) {
      return next();
    }
    return router.push({ name: 'index.signup' });
  }

  return next();
});

'init' 操作应该返回一个承诺:

const actions = {
  async init({commit}) {
    const user = await service.getUser();
    commit('setUser', user);
  }
}

这种方法存在的问题是,每当我们导航到给定页面时,它都会触发“init”操作,该操作将从服务器获取用户。我们只想在没有用户的情况下获取用户,因此我们可以更新商店检查它是否有用户并相应地获取它:

const state = {
  user: null
}
const actions = {
  async init({commit, state}) {
    if(!state.user) {
      const user = await service.getUser();
      commit('setUser', user);
    }
  }
}