web-dev-qa-db-fra.com

Séparation des magasins vuex pour les composants créés dynamiquement

C'était la question qui m'a un peu coincé. Malheureusement, je n'ai pas trouvé de réponse ici ( demander n'a pas aidé non plus). Donc, après avoir fait des recherches et demandé ici et là, il semble que j'ai trouvé la solution à ce problème.

Si vous avez une question à laquelle vous connaissez déjà la réponse, et que vous souhaitez documenter cette connaissance en public afin que d'autres (y compris vous-même) puissent la trouver plus tard.

Bien sûr, ma réponse n'est peut-être pas idéale, d'ailleurs je sais que ce n'est pas le cas, c'est le point clé pour lequel je poste - pour l'améliorer.

Remarque, je n'utilise pas d'actions dans l'exemple. L'idée est la même.

Commençons par énonçant le problème:

Imaginez que nous avons App.vue qui génère dynamiquement son composant local nommé Hello.

<template>
  <div id="app">
    <div>
      <hello v-for="i in jobs" :key="i" :id="i"></hello>
      <button @click="addJob">New</button>
    </div>
  </div>
</template>   

<script>
import Hello from './components/Hello'

export default {
  components: {
    Hello
  }...

store.js

export const store = new Vuex.Store({
  state: {
    jobs: []
  }
})

Nous utilisons v-for directive pour générer des composants en itérant à travers un tableau jobs. Notre store ne se compose désormais que de state avec un tableau vide. Le bouton New devrait faire 2 choses:

1) créer un nouveau composant Hello, en d'autres termes, ajouter un élément à jobs (que ce soit des nombres), qui vont être affectés comme key et id de <hello>, et passé au composant local en tant que props.

2) générer des magasins locaux - modules - pour conserver toutes les données composants nouvellement créés.

Hello.vue

<template>
  <div>
    <input type="number" :value="count">
    <button @click="updateCountPlus">+1</button>
  </div>
</template>

export default {
  props: ['id']
}

Composant simple - entrée avec un bouton ajoutant 1.

Notre objectif est de concevoir quelque chose comme ceci: Vuex local stores

25
ogostos

Pour la première opération du bouton NEW - génération de composants - nous ajoutons mutation à notre store.js

 mutations: {
    addJob (state) {
      state.jobs.Push(state.jobs.length + 1)
...
}

Deuxièmement, création de modules locaux. Ici, nous allons utiliser reusableModule pour générer plusieurs instances d'un module. Ce module que nous conservons dans un fichier séparé pour plus de commodité. De plus, notez l'utilisation de la fonction pour déclarer l'état du module .

const state = () => {
  return {
    count: 0
  }
}

const getters = {
  count: (state) => state.count
}

const mutations = {
  updateCountPlus (state) {
    state.count++
  }
}

export default {
  state,
  getters,
  mutations
}

Pour utiliser reusableModule, nous l'importons et appliquons l'enregistrement de module dynamique.

store.js

import module from './reusableModule'

const {state: stateModule, getters, mutations} = module

export const store = new Vuex.Store({
  state: {
    jobs: []
  },
  mutations: {
    addJob (state) {
      state.jobs.Push(state.jobs.length + 1)
      store.registerModule(`module${state.jobs.length}`, {
        state: stateModule,
        getters,
        mutations,
        namespaced: true // making our module reusable
      })
    }
  }
})

Après, nous allons lier Hello.vue avec son rangement. Nous pouvons avoir besoin de state, getters, mutations, actions de vuex. Pour accéder au stockage, nous devons créer notre getters. Idem avec mutations.

Home.vue

<script>

export default {
  props: ['id'],
  computed: {
     count () {
        return this.$store.getters[`module${this.id}/count`]
     }
  },
  methods: {
    updateCountPlus () {
        this.$store.commit(`module${this.id}/updateCountPlus`)
     } 
  }
}
</script>

Imaginez que nous ayons beaucoup de getters, mutations et actions. Pourquoi ne pas utiliser {mapGetters} ou {mapMutations}? Lorsque nous avons plusieurs modules et que nous savons le chemin d'accès au module nécessaire, nous pouvons le faire. Malheureusement, nous n'avons pas accès au nom du module.

Le code est exécuté lorsque le module du composant est exécuté (lorsque votre application démarre), et non lorsque le composant est créé. Ces assistants ne peuvent donc être utilisés que si vous connaissez à l'avance le nom du module.

Il y a peu d'aide ici. Nous pouvons séparer nos getters et mutations puis les importer en tant qu'objet et les garder propres.

<script>
import computed from '../store/moduleGetters'
import methods from '../store/moduleMutations'

export default {
  props: ['id'],
  computed,
  methods
}
</script>

Retour au composant App. Nous devons valider notre mutation et créons également un getter pour App. Pour montrer comment pouvons-nous accéder aux données situées dans les modules.

store.js

export const store = new Vuex.Store({
  state: {
    jobs: []
  },
  getters: {
    jobs: state => state.jobs,
    sumAll (state, getters) {
      let s = 0
      for (let i = 1; i <= state.jobs.length; i++) {
        s += getters[`module${i}/count`]
      }
      return s
    }
  } 
...

Code de finition dans le composant App

<script>
import Hello from './components/Hello'
import {mapMutations, mapGetters} from 'vuex'

    export default {
      components: {
        Hello
      },
      computed: {
        ...mapGetters([
          'jobs',
          'sumAll'
        ])
      },
      methods: {
        ...mapMutations([
          'addJob'
        ])
      }
    }
    </script>
20
ogostos

Bonjour et merci d'avoir posté votre question et votre solution.

J'ai commencé à apprendre Vuex il y a quelques jours et j'ai rencontré un problème similaire. J'ai vérifié votre solution et trouvé la mienne qui ne nécessite pas l'enregistrement de nouveaux modules. Je trouve que c'est assez exagéré et pour être honnête, je ne comprends pas pourquoi vous le faites. Il y a toujours une possibilité que j'ai mal compris le problème.

J'ai créé une copie de votre balisage avec quelques différences à des fins de clarté et de démonstration.

J'ai:

  1. JobList.vue - principal composant personnalisé
  2. Job.vue - Composant personnalisé enfant de la liste des tâches
  3. jobs.js - fichier du module de magasin vuex

JobList.vue (qui est responsable de l'habillage des éléments de la liste des travaux)

<template>
    <div>
        <job v-for="(job, index) in jobs" :data="job" :key="job.id"></job>

        <h3>Create New Job</h3>
        <form @submit.prevent="addJob">
            <input type="text" v-model="newJobName" required>
            <button type="submit">Add Job</button>
        </form>
    </div>
</template>

<script>
    import store from '../store/index'
    import job from './job';

    export default {
        components: { job },
        data() {
            return {
                newJobName: ''
            };
        },
        computed: {
            jobs() {
                return store.state.jobs.jobs;
            }
        },
        methods: {
            addJob() {
                store.dispatch('newJob', this.newJobName);
            }
        }
    }
</script>

Le travail

<template>
    <div>
        <h5>Id: {{ data.id }}</h5>
        <h4>{{ data.name }}</h4>
        <p>{{ data.active}}</p>
        <button type="button" @click="toggleJobState">Toggle</button>
        <hr>
    </div>
</template>

<script>

    import store from '../store/index'

    export default {
        props: ['data'],
        methods: {
            toggleJobState() {
                store.dispatch('toggleJobState', this.data.id);
            }
        }
    }

</script>

Et enfin le fichier du module jobs.js Vuex:

export default {
    state: {
        jobs: [
            {
                id: 1,
                name: 'light',
                active: false
            },
            {
                id: 2,
                name: 'medium',
                active: false
            },
            {
                id: 3,
                name: 'heavy',
                active: false
            }
        ]
    },

    actions: { //methods
        newJob(context, jobName) {
            context.state.jobs.Push({
                id: context.getters.newJobId,
                name: jobName,
                active: false
            });
        },
        toggleJobState(context, id) {
            context.state.jobs.forEach((job) => {
                if(job.id === id) { job.active = !job.active; }
            })
        }
    },

    getters: { //computed properties
        newJobId(state) { return state.jobs.length + 1; }
    }
}

Il est possible d'ajouter de nouveaux travaux au magasin et comme le suggère la propriété "active", vous pouvez contrôler chaque travail individuel sans avoir besoin d'un nouveau module vuex personnalisé.

2
Vlada