Imprimer
Catégorie : R3.01 - dev. Web côté client (vuejs)
Affichages : 817

L'archive des sources (répertoire src) pour les démonstrations est téléchargeable [ ici ]


 

 1°/ Objectifs de Vuex

 

ATTENTION ! Ce n'est pas parce que l'on utilise Vuex que l'on va mettre toutes les données dans le store. Les composants peuvent toujours définir des variables locales (dans data ou computed).

 

2°/ Principes basiques de Vuex

 

 

3°/ Créer un store

3.1°/ state et mutations

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

 

mutations: {
    increment(state, amount) {
        state.count += amount
    }
}

 

ATTENTION ! les méthodes de mutations doivent être synchrones. Cela signifie que leur code ne doit pas contenir des appels à des méthodes dont le moteur JS va attendre le résultat dans le futur, sans savoir quand. Par exemple, il est interdit de faire un appel à axios pour récupérer des données auprès d'une API REST.

 

3.2°/ Les getters

Exemple :

{
  state: () => ({	count:0 }),
  getters: {
    doubleCount(state) { return state.count*2 },
    fifthCount : (state, getters) => (state.count+getters.doubleCount*2)
  }
}
 
3.3°/ Les actions
  • Le fait que les mutations fassent des traitement synchrones peut poser problème, notamment lorsque l'on doit récupérer des données de façon asynchrone (par ex, via axios).
  • Dans ce cas, il faut définir une méthode dans la propriété actions du store.
  • Cette méthode prend en paramètre un objet qui est une sorte de copie du store, que l'on appelle un contexte dans le jargon Vuex. C'est pourquoi on nomme généralement ce paramètre context.
  • Cet objet permet d'accéder aux mutations, et à state (et si besoin getters), et ainsi faire le traitement asynchrone nécessaire pour mettre à jour state.
  • Comme pour les mutations, une action peut prendre un deuxième paramètre.
  • Quand une action veut utiliser une mutation, on utilise la fonction commit() de context, avec en paramètre le nom de la mutation et éventuellement un paramètre.

ATTENTION : une action ne modifie JAMAIS state directement (même si c'est techniquement possible). Pour cela elle utilise les mutations existantes.

Exemple :

  • Dans store/index.js :
  state: () => ({
	count:0
  }),
  mutations: {
	increment(state, amount) {
	    state.count+= amount
	}
  },
  actions: {
     incrementAsync(context, amount) {
         setTimeout(() => {
             context.commit('increment',amount)
       }, 1000)
     }
  }
 

4°/ accès et manipulation du store

4.1°/ $store

 

Exemple :

<template>
  <div>
      {{ count }}
  </div>
</template>
<script>
  export default {
    computed: {
      count() { return this.$store.state.count }
    },
    methods: {
        updateCount(val) { this.$store.commit('updateCount', val) }
    }
  }
</script>

 

Remarque : dans cet exemple, on utilise le même nom pour la variable calculée et celle du store. Ce n'est pas une obligation. De même pour la méthode et la mutation.

 

4.2°/ Les mappers.

 

 Exemple :

state: () => ({
	count:0,
	tab: []
   }),
mutations: {
  increment(state, amount) {
	    state.count+= amount
  },
  store(state, value) {
    state.tab.push(value)
  }
},
getters: {
  doubleCount(state)  { return state.count*2},
  fifthCount: (state,getters) => (state.count + getters.doubleCount*2),
  getItem: (state) => (id) => (state.tab[id]) //renvoie une fonction qui elle-même renvoie tab[id]
},
actions: {
   incrementAsync(context, amount) {
     setTimeout(() => {
          context.commit('increment',amount)
      }, 2000)
   }
}
<template>
  <div class="about">
    <h1>This is ComponentA</h1>
    {{ count }} {{ doubleCount }} {{ fifthCount }}
    <button @click="increment(2)">Inc by 2</button>
    <button @click="incrementAsync(3)">Inc async by 3</button>
    <hr />
    <button @click="store(count)">Store current value</button>
    tab = {{ tab }}, tab value in <input v-model="index"> : {{ getItem(index) }}
    <hr />
    index: {{ index }}, modified: {{ plusFive }}
  </div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'

export default {
  name: 'ComponentA',
  data: () => {
    return {
      index: 0
    }
  },
  computed: {
     plusFive() { return this.index+5 }, 
     ...mapState([ 'count', 'tab']),
     ...mapGetters(['doubleCount','fifthCount','getItem'])

  },
  methods: {
     ...mapMutations(['increment','store']),
     ...mapActions(['incrementAsync'])
  }
}
</script>

 

Remarques :

Démonstration 1 :

 

5°/ Les modules

 

 

Par exemple :

export default {
  namespaced : true,
   state: () => ({
	count:0
    }),
    mutations: {
	increment(state, amount) {
	    state.count+= amount
	}
  }
}
export default {
  namespaced : true,
   state: () => ({
	tab: []
  }),
  mutations: {
	store(state, value) {
	    state.tab.push(value)
    }
  },
  getters: {
	getItem: (state) => (id) => (state.tab[id])
  },
  actions: {
    storeAndIncrement({commit}, value) {
      commit('store',value)
      commit('module1/increment',value,{root: true})
    }
  }
}
...
import module1 from './module1.js'
import module2 from './module2.js'

export default new Vuex.Store({
    modules: {
      module1,
      module2
    }
})
<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'

export default {
  ...
  computed: {     
     ...mapState('module1',['count']),
     ...mapState('module2',['tab']),     
     ...mapGetters('module2',['getItem'])     
  },
  methods: {
     ...mapMutations('module1',['increment']),
     ...mapMutations('module2',['store']),
      ...mapActions('modules2',['storeAndIncrement'])     
  }
}
</script>

 

Démonstration 2: