Vue.js
Vuex
Ricerca…
introduzione
Vuex è una libreria di gestione dello stato + libreria per le applicazioni Vue.js. Serve da archivio centralizzato per tutti i componenti di un'applicazione, con regole che garantiscono che lo stato possa essere mutato solo in modo prevedibile. Si integra inoltre con l'estensione ufficiale degli strumenti di sviluppo di Vue per fornire funzionalità avanzate quali il debugging della corsa a zero zero e l'esportazione / importazione dello snapshot di stato.
Cos'è Vuex?
Vuex è un plug-in ufficiale per Vue.js che offre un archivio dati centralizzato da utilizzare all'interno dell'applicazione. È fortemente influenzato dall'architettura dell'applicazione Flux che presenta un flusso di dati unidirezionale che porta a una progettazione e un ragionamento delle applicazioni più semplici.
All'interno di un'applicazione Vuex, il datastore contiene tutti gli stati di applicazione condivisi . Questo stato è alterato dalle mutazioni che vengono eseguite in risposta a un'azione che richiama un evento di mutazione tramite il dispatcher .
Un esempio del flusso di dati in un'applicazione Vuex è descritto nello schema seguente. Diagramma utilizzato con la licenza MIT , originariamente dal repository ufficiale Vuex GitHub .
Singoli componenti dell'applicazione Vue.js possono accedere all'oggetto negozio per recuperare i dati tramite getter , che sono funzioni pure che restituiscono una copia di sola lettura dei dati desiderati.
I componenti possono avere azioni che sono funzioni che eseguono modifiche alla propria copia dei dati del componente, quindi utilizzare il dispatcher per inviare un evento di mutazione. Questo evento viene quindi gestito dal datastore che aggiorna lo stato secondo necessità.
Le modifiche vengono quindi automaticamente riflesse in tutta l'applicazione poiché tutti i componenti sono legati in modo reattivo allo store tramite i loro getter.
Un esempio che illustra l'uso di vuex in un progetto vue.
const state = {
lastClickTime: null
}
const mutations = {
updateLastClickTime: (state, payload) => {
state.lastClickTime = payload
}
}
const getters = {
getLastClickTime: state => {
return new Date(state.lastClickTime)
}
}
const actions = {
syncUpdateTime: ({ commit }, payload) => {
commit("updateLastClickTime", payload)
},
asyncUpdateTime: ({ commit }, payload) => {
setTimeout(() => {
commit("updateLastClickTime", payload)
}, Math.random() * 5000)
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions
})
const { mapActions, mapGetters } = Vuex;
// Vue
const vm = new Vue({
el: '#container',
store,
computed: {
...mapGetters([
'getLastClickTime'
])
},
methods: {
...mapActions([
'syncUpdateTime',
'asyncUpdateTime'
]),
updateTimeSyncTest () {
this.syncUpdateTime(Date.now())
},
updateTimeAsyncTest () {
this.asyncUpdateTime(Date.now())
}
}
})
E il modello HTML per lo stesso:
<div id="container">
<p>{{ getLastClickTime || "No time selected yet" }}</p>
<button @click="updateTimeSyncTest">Sync Action test</button>
<button @click="updateTimeAsyncTest">Async Action test</button>
</div>
Qui lo stato contiene la proprietà lastClickTime inizializzata come null. Questa impostazione di valori predefiniti è importante per mantenere reattive le proprietà. Le proprietà non menzionate nello stato saranno disponibili ma le modifiche apportate in seguito non sarebbero accessibili utilizzando i getter.
Il getter utilizzato fornisce una proprietà calcolata che verrà aggiornata ogni volta che una mutazione aggiorna il valore della proprietà di stato.
Solo le mutazioni possono cambiare lo stato e le sue proprietà, ciò detto, lo fa solo in modo sincrono .
Un'Azione può essere usata in caso di aggiornamenti asincroni, dove la chiamata API (qui derisa dal setTimeout a tempo casuale) può essere fatta nell'azione, e dopo aver ottenuto la risposta a cui può essere impegnata una mutazione, per effettuare la modifica allo stato .
Perché usare Vuex?
Quando si costruiscono applicazioni di grandi dimensioni come le applicazioni per pagina singola (SPA), che in genere sono costituite da molti componenti riutilizzabili, possono diventare rapidamente difficili da costruire e gestire. Anche la condivisione di dati e stato tra questi componenti può essere rapidamente interrotta e diventa difficile eseguirne il debug e la manutenzione.
Utilizzando un archivio dati centralizzato dell'applicazione, l'intero stato dell'applicazione può essere rappresentato in un unico punto, rendendo l'applicazione più organizzata. Attraverso l'uso di un flusso di dati unidirezionale, le mutazioni e l'accesso ai dati del componente scoping solo ai dati richiesti, diventa molto più semplice ragionare sul ruolo del componente e su come dovrebbe influire sullo stato dell'applicazione.
I componenti VueJS sono entità separate e non possono condividere facilmente dati tra loro. Per condividere i dati senza vuex, dobbiamo emit
eventi con dati e quindi ascoltare e catturare quell'evento con on
.
componente 1
this.$emit('eventWithDataObject', dataObject)
componente 2
this.$on('eventWithDataObject', function (dataObject) {
console.log(dataObject)
})
Con vuex installato possiamo semplicemente accedere ai suoi dati da qualsiasi componente senza bisogno di ascoltare gli eventi.
this.$store.state.myData
Possiamo inoltre modificare i dati in modo sincrono con i mutatori , utilizzare azioni asincrone e ottenere dati con le funzioni getter .
Le funzioni Getter potrebbero funzionare come funzioni globali calcolate. Possiamo accedervi dai componenti:
this.$store.getters.myGetter
Le azioni sono metodi globali. Possiamo spedirli dai componenti:
this.$store.dispatch('myAction', myDataObject)
E le mutazioni sono l'unico modo per cambiare i dati in vuex. Siamo in grado di commettere modifiche:
this.$store.commit('myMutation', myDataObject)
Il codice Vuex sarebbe simile a questo
state: {
myData: {
key: 'val'
}
},
getters: {
myGetter: state => {
return state.myData.key.length
}
},
actions: {
myAction ({ commit }, myDataObject) {
setTimeout(() => {
commit('myMutation', myDataObject)
}, 2000)
}
},
mutations: {
myMutation (state, myDataObject) {
state.myData = myDataObject
}
}
Come installare Vuex?
La maggior parte delle volte che utilizzerai Vuex sarà in applicazioni basate su componenti più grandi, dove probabilmente utilizzerai un bundle di moduli come Webpack o Browserify insieme a Vueify se utilizzi file singoli.
In questo caso il modo più semplice per ottenere Vuex è da NPM. Eseguire il comando seguente per installare Vuex e salvarlo nelle dipendenze dell'applicazione.
npm install --save vuex
Assicurati di caricare il link Vuex con il tuo setup Vue posizionando la seguente riga dopo la tua require('vue')
.
Vue.use(require('vuex'))
Vuex è anche disponibile su CDN; si può afferrare l'ultima versione dal cdnjs qui .
Notifiche auto licenziabili
Questo esempio registra dinamicamente un modulo vuex per la memorizzazione di notifiche personalizzate che possono essere automaticamente eliminate
notifications.js
Risolvi vuex store e definisci alcune costanti
//Vuex store previously configured on other side
import _store from 'path/to/store';
//Notification default duration in milliseconds
const defaultDuration = 8000;
//Valid mutation names
const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED';
const NOTIFICATION_DISMISSED = 'NOTIFICATION_DISMISSED';
imposta il nostro stato iniziale del modulo
const state = {
Notifications: []
}
imposta i getter del modulo
const getters = {
//All notifications, we are returning only the raw notification objects
Notifications: state => state.Notifications.map(n => n.Raw)
}
imposta il nostro modulo Azioni
const actions = {
//On actions we receive a context object which exposes the
//same set of methods/properties on the store instance
//{commit} is a shorthand for context.commit, this is an
//ES2015 feature called argument destructuring
Add({ commit }, notification) {
//Get notification duration or use default duration
let duration = notification.duration || defaultDuration
//Create a timeout to dismiss notification
var timeOut = setTimeout(function () {
//On timeout mutate state to dismiss notification
commit(NOTIFICATION_DISMISSED, notification);
}, duration);
//Mutate state to add new notification, we create a new object
//for save original raw notification object and timeout reference
commit(NOTIFICATION_ADDED, {
Raw: notification,
TimeOut: timeOut
})
},
//Here we are using context object directly
Dismiss(context, notification) {
//Just pass payload
context.commit(NOTIFICATION_DISMISSED, notification);
}
}
imposta le nostre mutazioni di modulo
const mutations = {
//On mutations we receive current state and a payload
[NOTIFICATION_ADDED](state, notification) {
state.Notifications.push(notification);
},
//remember, current state and payload
[NOTIFICATION_DISMISSED](state, rawNotification) {
var i = state.Notifications.map(n => n.Raw).indexOf(rawNotification);
if (i == -1) {
return;
}
clearTimeout(state.Notifications[i].TimeOut);
state.Notifications.splice(i, 1);
}
}
Registra il nostro modulo con stato, getter, azioni e mutazione definiti
_store.registerModule('notifications', {
state,
getters,
actions,
mutations
});
uso
componentA.vue
Questo componente visualizza tutte le notifiche come avvisi di bootstrap nell'angolo in alto a destra dello schermo, inoltre consente di ignorare manualmente ciascuna notifica.
<template>
<transition-group name="notification-list" tag="div" class="top-right">
<div v-for="alert in alerts" v-bind:key="alert" class="notification alert alert-dismissible" v-bind:class="'alert-'+alert.type">
<button v-on:click="dismiss(alert)" type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
<div>
<div>
<strong>{{alert.title}}</strong>
</div>
<div>
{{alert.text}}
</div>
</div>
</div>
</transition-group>
</template>
<script>
export default {
name: 'arc-notifications',
computed: {
alerts() {
//Get all notifications from store
return this.$store.getters.Notifications;
}
},
methods: {
//Manually dismiss a notification
dismiss(alert) {
this.$store.dispatch('Dismiss', alert);
}
}
}
</script>
<style lang="scss" scoped>
$margin: 15px;
.top-right {
top: $margin;
right: $margin;
left: auto;
width: 300px;
//height: 600px;
position: absolute;
opacity: 0.95;
z-index: 100;
display: flex;
flex-wrap: wrap;
//background-color: red;
}
.notification {
transition: all 0.8s;
display: flex;
width: 100%;
position: relative;
margin-bottom: 10px;
.close {
position: absolute;
right: 10px;
top: 5px;
}
> div {
position: relative;
display: inline;
}
}
.notification:last-child {
margin-bottom: 0;
}
.notification-list-enter,
.notification-list-leave-active {
opacity: 0;
transform: translateX(-90px);
}
.notification-list-leave-active {
position: absolute;
}
</style>
Snippet per aggiungere notifiche in qualsiasi altro componente
//payload could be anything, this example content matches with componentA.vue
this.$store.dispatch('Add', {
title = 'Hello',
text = 'World',
type = 'info',
duration = 15000
});