web-dev-qa-db-fra.com

Comment accéder aux données du magasin asynchrone dans vue-router pour une utilisation dans le hook beforeEnter?

Comment est-il possible d'accéder aux données du magasin dans beforeEnter qui sont récupérées de manière asynchrone via l'action de magasin?

import store from './vuex/store';

store.dispatch('initApp'); // in here, async data will be fetched and assigned to the store's state

// following is an excerpt of the routes object:
{
  path: '/example',
  component: Example,
  beforeEnter: (to, from, next) =>
  {
    if (store.state.asyncData) {
      // the above state is not available here, since it
      // it is resolved asynchronously in the store action
    }
  }
}

Ceci est particulièrement important lors du premier chargement de page ou après un rechargement de page, lorsque les données d'initialisation sont récupérées et que le routeur doit attendre que ces données permettent à l'utilisateur d'accéder à cette page ou non.

Est-il possible que le routeur "attende" que les données soient récupérées? Ou quelle est la meilleure façon de gérer la protection de navigation en combinaison avec les données du magasin async vuex?

(oh et pré-remplir "asyncData" ne peut pas être la solution, car le hook beforeEnter doit prendre sa décision sur les données réelles de la base de données, pas les données par défaut)

12
santacruz

Vous pouvez le faire en renvoyant une promesse de l'action vuex, comme cela est expliqué ici et appelez le dispatch depuis le beforeEnter lui-même.

Le code devrait ressembler à ceci:

import store from './vuex/store';


// following is an excerpt of the routes object:
{
  path: '/example',
  component: Example,
  beforeEnter: (to, from, next) =>
  {
    store.dispatch('initApp').then(response => {
        // the above state is not available here, since it
        // it is resolved asynchronously in the store action
    }, error => {
        // handle error here
    })         
  }
}
13
Saurabh

Avez-vous vraiment besoin de récupérer les données de manière asynchrone à partir du serveur avant chaque changement d'itinéraire, ou avez-vous simplement besoin de conserver certaines données du magasin afin qu'elles ne "disparaissent" pas lorsque la page est rechargée ou lorsque l'utilisateur utilise un lien direct?

Si ce dernier est le cas, vous pouvez conserver les données d'authentification (par exemple le jeton JWT) dans localStorage/cookie , puis les retirer simplement du stockage pendant l'initialisation (et engagez-les à stocker) - cela devrait être une action synchrone.

Vous pouvez également conserver la totalité de l'état du magasin en utilisant vuex-persistedstate afin qu'il ne disparaisse pas lors du rechargement et n'a pas besoin d'être hydraté.

Si vous devez récupérer certaines données de manière asynchrone une fois avant le premier chargement ou le rechargement de la page, vous pouvez envoyer une action de stockage et initialiser le Vue instance dans le rappel then() - mais cela peut dépendre de votre implémentation. Quelque chose comme ça (dans main.js):

import Vue from 'vue';
import VueRouter from 'vue-router';
import { sync } from 'vuex-router-sync';

// contains VueRouter instance and route definitions
import router from 'src/router/router';
// contains Vuex.Store instance. I guess this is './vuex/store' in your example?
import store from 'src/store/store';

// sync the router with vuex store using vuex-router-sync
Vue.use(VueRouter);
sync(store, router);

// dispatch init action, doing asynchronous stuff, commiting to store
store.dispatch('initApp').then(() => {
    // create the main Vue instance and mount it
    new Vue({
        router,
        store           
    }).$mount('#app');
});
7
Arx

J'ai résolu ce problème en utilisant store.watch () pour aucune valeur initiale et renvoyer la dernière valeur lorsqu'elle est déjà initialisée.

Voici mon exemple de code

async function redirectIfNotAuth (to, from, next) {
  const user = await getUserState()
  if (user === null) {
    next({ name: 'auth' })
  } else {
    next()
  }
}

function getUserState () {
  return new Promise((resolve, reject) => {
    if (store.state.user === undefined) {
      const unwatch = store.watch(
        () => store.state.user,
        (value) => {
          unwatch()
          resolve(value)
        }
      )
    } else {
      resolve(store.state.user)
    }
  })
}


/* STORE */
const store = new Vuex.Store({
  state: {
    user: undefined
  }
})

/* ROUTER */
new Router({
  routes: [
    {
      path: '',
      name: 'home',
      component: Home,
      beforeEnter: redirectIfNotAuth
    },
    {
      path: '/signin',
      name: 'auth',
      component: Auth,
    }
  ]
})
5
Jeng Wittawat