vue.js 2 wie man Speicherwerte von vuex überwacht


167

Ich benutze vuexund vuejs 2zusammen.

Ich bin neu in vuex, ich möchte eine storeVariablenänderung beobachten.

Ich möchte die watchFunktion in meinem hinzufügenvue component

Das habe ich bisher:

import Vue from 'vue';
import {
  MY_STATE,
} from './../../mutation-types';

export default {
  [MY_STATE](state, token) {
    state.my_state = token;
  },
};

Ich möchte wissen, ob es Änderungen in der gibt my_state

Wie store.my_stateschaue ich in meiner vuejs-Komponente?


Verwenden Sie ein Vuejs-Plugin, das mit Chrom
geliefert

Antworten:


204

Angenommen, Sie haben einen Obstkorb, und jedes Mal, wenn Sie eine Frucht zum Korb hinzufügen oder daraus entfernen, möchten Sie (1) Informationen zur Anzahl der Früchte anzeigen, aber auch (2) benachrichtigt werden die Zählung der Früchte auf ausgefallene Weise ...

Fruchtzählung-Komponente.vue

<template>
  <!-- We meet our first objective (1) by simply -->
  <!-- binding to the count property. -->
  <p>Fruits: {{ count }}</p>
</template>

<script>
import basket from '../resources/fruit-basket'

export default () {
  computed: {
    count () {
      return basket.state.fruits.length
      // Or return basket.getters.fruitsCount
      // (depends on your design decisions).
    }
  },
  watch: {
    count (newCount, oldCount) {
      // Our fancy notification (2).
      console.log(`We have ${newCount} fruits now, yay!`)
    }
  }
}
</script>

Bitte beachten Sie, dass der Name der Funktion im watchObjekt mit dem Namen der Funktion im computedObjekt übereinstimmen muss . Im obigen Beispiel lautet der Name count.

Neue und alte Werte einer überwachten Eigenschaft werden als Parameter an den Überwachungsrückruf (die Zählfunktion) übergeben.

Der Korbladen könnte so aussehen:

Obstkorb.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const basket = new Vuex.Store({
  state: {
    fruits: []
  },
  getters: {
    fruitsCount (state) {
      return state.fruits.length
    }
  }
  // Obviously you would need some mutations and actions,
  // but to make example cleaner I'll skip this part.
})

export default basket

Weitere Informationen finden Sie in den folgenden Ressourcen:


Ich frage mich nur, was ich tun soll, wenn die watchAktion in zwei Schritte aufgeteilt werden soll: 1) Überprüfen Sie zunächst, ob die gewünschten Daten zwischengespeichert sind und ob nur die zwischengespeicherten Daten zurückgegeben werden. 2) Wenn der Cache fehlgeschlagen ist, benötige ich eine asynchrone Ajax-Aktion, um die Daten abzurufen, aber dies scheint die actionArbeit von zu sein. Ich hoffe, meine Frage macht Sinn, danke!
1Cr18Ni9

Was ist der Vorteil davon gegenüber der Antwort von micah5, die nur einen Beobachter in der Komponente auf den Speicherwert setzt? Es muss weniger Code gepflegt werden.
Exozentrisch

@Exocentric Als ich die Antwort schrieb, war mir die Frage nicht klar. Es gibt keinen Kontext, warum es erforderlich ist, Eigenschaften zu überwachen. Stellen Sie sich das so vor: "Ich möchte die Variable X sehen, damit ich Y machen kann." Wahrscheinlich schlagen die meisten Antworten deshalb so sehr unterschiedliche Ansätze vor. Niemand weiß, was die Absicht ist. Deshalb habe ich "Ziele" in meine Antwort aufgenommen. Wenn Sie unterschiedliche Ziele haben, passt möglicherweise eine andere Antwort dazu. Mein Beispiel ist nur ein Ausgangspunkt für Experimente. Es ist nicht als Plug & Play-Lösung gedacht. Es gibt keinen "Vorteil", da der Nutzen von Ihrer Situation abhängt.
Anastazy

@ 1Cr18Ni9 Ich denke, Caching gehört nicht in den Komponentencode. Sie werden am Ende etwas überentwickeln, das wirklich einfach sein sollte (Daten abrufen und an die Ansicht binden). Caching ist bereits im Browser implementiert. Sie können dies nutzen, indem Sie korrekte Header vom Server senden. Einfache Erklärung hier: csswizardry.com/2019/03/cache-control-for-civilians . Sie können sich auch ServiceWorkers ansehen, mit denen die Website auch ohne Internetverbindung funktioniert.
Anastazy

60

Sie sollten die Beobachter der Komponente nicht verwenden, um Statusänderungen abzuhören. Ich empfehle Ihnen, Getter-Funktionen zu verwenden und diese dann in Ihrer Komponente zuzuordnen.

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters({
      myState: 'getMyState'
    })
  }
}

In Ihrem Geschäft:

const getters = {
  getMyState: state => state.my_state
}

Sie sollten in der Lage sein, Änderungen an Ihrem Geschäft zu hören, indem Sie sie this.myStatein Ihrer Komponente verwenden.

https://vuex.vuejs.org/en/getters.html#the-mapgetters-helper


1
Ich weiß nicht, wie man mapGetters implementiert. Können Sie mich auf ein Beispiel verweisen? Es wäre eine große Hilfe. Ich implementiere gerade GONG Antwort im Moment. TY
Rbex

1
@Rbex "mapGetters" ist Teil der 'vuex'-Bibliothek. Sie müssen es nicht implementieren.
Gabriel Robert

69
Diese Antwort ist einfach falsch. Er muss tatsächlich die berechneten Eigenschaften beobachten.
Juan

15
Der einmal aufgerufene Getter ruft nur den Status zu diesem Zeitpunkt ab. Wenn diese Eigenschaft die Statusänderung einer anderen Komponente widerspiegeln soll, müssen Sie sie beobachten.
C Tierney

3
Warum "Sie sollten die Beobachter der Komponente nicht verwenden, um Statusänderungen abzuhören"? Hier ist ein Beispiel, an das Sie vielleicht nicht denken, wenn ich ein Token aus dem Status überwachen möchte und wenn es sich ändert, um auf eine andere Seite umzuleiten. Es gibt also einige Fälle, in denen Sie dies tun müssen. Vielleicht brauchen Sie mehr Erfahrung, um das zu wissen.
Shlomi Levi

43

Es ist so einfach wie:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}

6
Dies ist ein verdammter Anblick, der einfacher ist als jede der Antworten hier ... gibt es ein Argument dagegen ...?
Inigo

19
Es ist zu einfach, sieht also nicht nach js aus, js muss komplizierter sein.
Digout

1
Wäre noch einfacher, wenn es wärefunction(n) { console.log(n); }
WofWca

2
Super cool. Interessiert sich auch für einen Nachteil dieses Ansatzes. Bisher scheint es gut zu funktionieren.
namero999

1
Ernsthaft. Dies scheint viel besser zu sein als die akzeptierte Antwort, für die doppelte Funktionsnamen in watch und berechnet erforderlich sind. Kann ein Experte kommentieren, warum oder warum dies nicht so gemacht wird?
Exozentrisch

42

Wie oben erwähnt, ist es nicht ratsam, Änderungen direkt im Geschäft zu beobachten

Aber in einigen sehr seltenen Fällen kann es für jemanden nützlich sein, also werde ich diese Antwort hinterlassen. Für andere Fälle siehe @ gabriel-robert Antwort

Sie können dies durch tun state.$watch. Fügen Sie dies in Ihre created(oder wo Sie dies ausführen müssen) Methode in der Komponente ein

this.$store.watch(
    function (state) {
        return state.my_state;
    },
    function () {
        //do something on data change
    },
    {
        deep: true //add this if u need to watch object properties change etc.
    }
);

Weitere Details: https://vuex.vuejs.org/api/#watch


3
Ich denke nicht, dass es eine gute Idee ist, den Zustand direkt zu beobachten. Wir sollten Getter verwenden. vuex.vuejs.org/en/getters.html#the-mapgetters-helper
Gabriel Robert

14
@ GabrielRobert Ich denke, es gibt einen Platz für beide. Wenn Sie die Vorlagenbedingungen basierend auf Änderungen reaktiv ändern müssen, ist die Verwendung eines berechneten Werts mit mapState usw. sinnvoll. Ansonsten benötigen Sie, wie bei einer gleichmäßigen Flusskontrolle in einer Komponente, eine vollständige Überwachung. Sie haben Recht, Sie sollten keine einfachen Komponentenbeobachter verwenden, sondern den Status. $ Watch ist für diese Anwendungsfälle ausgelegt
Roberto Tomás

14
Jeder erwähnt es, aber niemand sagt warum! Ich versuche, einen Vuex-Speicher zu erstellen, der bei Änderungen automatisch mit einer Datenbank synchronisiert wird. Ich denke, Beobachter im Laden sind der reibungsloseste Weg! Was denken Sie? Immer noch keine gute Idee?
Mesqueeb

16

Ich denke, der Fragesteller möchte die Uhr mit Vuex verwenden.

this.$store.watch(
      (state)=>{
        return this.$store.getters.your_getter
      },
      (val)=>{
       //something changed do something

      },
      {
        deep:true
      }
      );

13

Dies ist für alle Leute gedacht, die ihr Problem mit Gettern nicht lösen können und tatsächlich einen Beobachter benötigen, z. B. um mit Dingen von Drittanbietern zu sprechen (siehe Vue Watchers, wann Beobachter verwendet werden sollen).

Die Beobachter und berechneten Werte der Vue-Komponente funktionieren auch mit berechneten Werten. Bei vuex ist das nicht anders:

import { mapState } from 'vuex';

export default {
    computed: {
        ...mapState(['somestate']),
        someComputedLocalState() {
            // is triggered whenever the store state changes
            return this.somestate + ' works too';
        }
    },
    watch: {
        somestate(val, oldVal) {
            // is triggered whenever the store state changes
            console.log('do stuff', val, oldVal);
        }
    }
}

Wenn es nur darum geht, den lokalen und den globalen Status zu kombinieren, bietet das mapState-Dokument auch ein Beispiel:

computed: {
    ...mapState({
        // to access local state with `this`, a normal function must be used
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
    }
})

netter Hack, aber einfach zu langweilig, findest du nicht?
Martian2049

2
Es ist kein Hack, wenn es in den Dokumenten steht, oder? Aber dann, es ist kein Pro-Argument für vue / vuex entweder
dube

8

Wenn Sie Typoskript verwenden, können Sie:

import { Watch } from "vue-property-decorator";

..

@Watch("$store.state.something")
private watchSomething() {
   // use this.$store.state.something for access
   ...
}


Warum genau wurde das abgelehnt? Nur weil die Lösung für die Vue-Klassen-Komponente ist und der TO nach alten Vue-Klassen-Stilen gefragt hat? Ersteres finde ich vorzuziehen. Vielleicht könnte @Zhang Sol in der Einleitung erwähnen, dass dies explizit für die vue-class-Komponente ist?
JackLeEmmerdeur

Beachten Sie, warum ein Typoskript-Dekorateur einer so einfachen nativen Lösung wie dieser vorzuziehen ist
yann_yinn

6

Erstellen Sie einen lokalen Status Ihrer Geschäftsvariablen, indem Sie Wertänderungen beobachten und festlegen. Wenn sich die lokale Variable für das V-Modell mit Formulareingabe ändert, wird die Speichervariable nicht direkt mutiert .

data() {
  return {
    localState: null
  };
 },
 computed: {
  ...mapGetters({
    computedGlobalStateVariable: 'state/globalStateVariable'
  })
 },
 watch: {
  computedGlobalStateVariable: 'setLocalState'
 },
 methods: {
  setLocalState(value) {
   this.localState = Object.assign({}, value);
  }
 }

5

Der beste Weg, um Änderungen im Geschäft zu beobachten, ist die Verwendung, mapGetterswie Gabriel sagte. Es gibt jedoch einen Fall, in dem Sie dies nicht tun können, mapGettersz. B. wenn Sie mithilfe des Parameters etwas aus dem Speicher holen möchten:

getters: {
  getTodoById: (state, getters) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

In diesem Fall können Sie nicht verwenden mapGetters. Sie können stattdessen versuchen, Folgendes zu tun:

computed: {
    todoById() {
        return this.$store.getters.getTodoById(this.id)
    }
}

Wird aber leider todoById nur aktualisiert, wenn this.idgeändert wird

Wenn Sie in diesem Fall eine Komponentenaktualisierung wünschen, verwenden Sie this.$store.watch die von Gong bereitgestellte Lösung . Oder gehen Sie bewusst mit Ihrer Komponente um und aktualisieren this.idSie sie, wenn Sie sie aktualisieren müssen todoById.


Danke. Das ist genau mein Anwendungsfall, und tatsächlich kann der Getter dann nicht beobachtet werden ...
pscheit

5

Wenn Sie einfach zu wollen , sehen ein staatliches Eigentum und dann innerhalb der Komponente handeln entsprechend den Änderungen dieser Eigenschaft sehen dann das folgende Beispiel.

In store.js:

export const state = () => ({
 isClosed: false
})
export const mutations = {
 closeWindow(state, payload) {
  state.isClosed = payload
 }
}

In diesem Szenario erstelle ich eine booleanStatuseigenschaft, die ich an verschiedenen Stellen in der Anwendung ändern möchte:

this.$store.commit('closeWindow', true)

Wenn ich nun diese Statuseigenschaft in einer anderen Komponente beobachten und dann die lokale Eigenschaft ändern muss, würde ich Folgendes in den mountedHook schreiben :

mounted() {
 this.$store.watch(
  state => state.isClosed,
  (value) => {
   if (value) { this.localProperty = 'edit' }
  }
 )
}

Zuerst setze ich einen Watcher für die Eigenschaft state und dann verwende ich in der Rückruffunktion die Eigenschaft this, um die valuezu ändern localProperty.

Ich hoffe, es hilft!


3

Wenn Sie auf Bundesstaatsebene schauen möchten, können Sie dies folgendermaßen tun:

let App = new Vue({
    //...
    store,
    watch: {
        '$store.state.myState': function (newVal) {
            console.log(newVal);
            store.dispatch('handleMyStateChange');
        }
    },
    //...
});

Es ist keine gute Idee, store.stateÄnderungen durch dispatchStatusaktionen von Komponenten zu behandeln, da dieses Verhalten nur funktioniert, wenn Sie diese Komponente verwenden. Sie könnten auch mit einer Endlosschleife enden. Achten Sie darauf, dass store.stateÄnderungen nur selten verwendet werden, z. B. wenn Sie eine Komponente oder eine Seite haben, die eine auf store.stateÄnderungen basierende Aktion ausführen sollte, die mit dem berechneten mapState nur dann nicht verarbeitet werden kann, wenn Sie nicht mit newValuevs vergleichen könnenoldValue
Januartha

@ Januartha was ist dein Vorschlag zu diesem Problem dann?
Billal Begueradj

@ Andy ja natürlich seine Arbeit. Ich möchte nur wissen, warum Sie anrufen store.dispatch? Wenn Sie store.stateÄnderungen für store' why not handle it inside store.mutations vornehmen möchten?
Januartha

@BillalBEGUERADJ I prever dube Lösung ist sauberer
Januartha

@ Januartha, da vor einer Mutation möglicherweise ein Ajax-Aufruf erfolgt, verwende ich diesen store.dispatchzuerst. Zum Beispiel möchte ich bei jeder $store.state.countryÄnderung alle Städte aus einem Land abrufen , also füge ich dies dem Beobachter hinzu. Dann würde ich einen Ajax-Anruf schreiben: in store.dispatch('fetchCities')schreibe ich:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Andy

2

Sie können eine Kombination aus Vuex- Aktionen , Gettern , berechneten Eigenschaften und Beobachtern verwenden , um Änderungen an einem Vuex-Statuswert abzuhören.

HTML Quelltext:

<div id="app" :style='style'>
  <input v-model='computedColor' type="text" placeholder='Background Color'>
</div>

JavaScript-Code:

'use strict'

Vue.use(Vuex)

const { mapGetters, mapActions, Store } = Vuex

new Vue({
    el: '#app',
  store: new Store({
    state: {
      color: 'red'
    },
    getters: {
      color({color}) {
        return color
      }
    },
    mutations: {
      setColor(state, payload) {
        state.color = payload
      }
    },
    actions: {
      setColor({commit}, payload) {
        commit('setColor', payload)
      }
    }
  }),
  methods: {
    ...mapGetters([
        'color'
    ]),
    ...mapActions([
        'setColor'
    ])
  },
  computed: {
    computedColor: {
        set(value) {
        this.setColor(value)
      },
      get() {
        return this.color()
      }
    },
    style() {
        return `background-color: ${this.computedColor};`
    }
  },
  watch: {
    computedColor() {
        console.log(`Watcher in use @${new Date().getTime()}`)
    }
  }
})

Siehe JSFiddle-Demo .


1

Sie können auch die Store-Mutationen abonnieren:

store.subscribe((mutation, state) => {
  console.log(mutation.type)
  console.log(mutation.payload)
})

https://vuex.vuejs.org/api/#subscribe


Sie können dies im beforeMount () - Hook Ihrer Komponente auslösen und dann die eingehenden Mutationen mit einer if-Anweisung filtern. zB wenn (mutations.type == "names / SET_NAMES") {... etwas tun}
Alejandro

1

Erstellen Sie innerhalb der Komponente eine berechnete Funktion

computed:{
  myState:function(){
    return this.$store.state.my_state; // return the state value in `my_state`
  }
}

Jetzt kann der berechnete Funktionsname wie z

watch:{
  myState:function(newVal,oldVal){
    // this function will trigger when ever the value of `my_state` changes
  }
}

Die im vuexStatus vorgenommenen Änderungen my_statespiegeln sich in der berechneten Funktion wider myStateund lösen die Überwachungsfunktion aus.

Wenn der Status my_stateverschachtelte Daten hat, handlerhilft die Option mehr

watch:{
  myState:{
    handler:function(newVal,oldVal){
      // this function will trigger when ever the value of `my_state` changes
    },
    deep:true
  }
}

Dadurch werden alle verschachtelten Werte im Speicher überwacht my_state.


0

Sie können mapState auch in Ihrer vue-Komponente verwenden, um den Status aus dem Geschäft abzurufen.

In Ihrer Komponente:

computed: mapState([
  'my_state'
])

Wo my_stateist eine Variable aus dem Laden.


0

====== store =====
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    showRegisterLoginPage: true,
    user: null,
    allitem: null,
    productShow: null,
    userCart: null
  },
  mutations: {
    SET_USERS(state, payload) {
      state.user = payload
    },
    HIDE_LOGIN(state) {
      state.showRegisterLoginPage = false
    },
    SHOW_LOGIN(state) {
      state.showRegisterLoginPage = true
    },
    SET_ALLITEM(state, payload) {
      state.allitem = payload
    },
    SET_PRODUCTSHOW(state, payload) {
      state.productShow = payload
    },
    SET_USERCART(state, payload) {
      state.userCart = payload
    }
  },
  actions: {
    getUserLogin({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/users',
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_USERS', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    addItem({ dispatch }, payload) {
      let formData = new FormData()
      formData.append('name', payload.name)
      formData.append('file', payload.file)
      formData.append('category', payload.category)
      formData.append('price', payload.price)
      formData.append('stock', payload.stock)
      formData.append('description', payload.description)
      axios({
        method: 'post',
        url: 'http://localhost:3000/products',
        data: formData,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log('data hasbeen created ', data)
          dispatch('getAllItem')
        })
        .catch(err => {
          console.log(err)
        })
    },
    getAllItem({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/products'
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_ALLITEM', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    addUserCart({ dispatch }, { payload, productId }) {
      let newCart = {
        count: payload
      }
      // console.log('ini dari store nya', productId)

      axios({
        method: 'post',
        url: `http://localhost:3000/transactions/${productId}`,
        data: newCart,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          dispatch('getUserCart')
          // console.log('cart hasbeen added ', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    getUserCart({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/transactions/user',
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_USERCART', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    cartCheckout({ commit, dispatch }, transactionId) {
      let count = null
      axios({
        method: 'post',
        url: `http://localhost:3000/transactions/checkout/${transactionId}`,
        headers: {
          token: localStorage.getItem('token')
        },
        data: {
          sesuatu: 'sesuatu'
        }
      })
        .then(({ data }) => {
          count = data.count
          console.log(count, data)

          dispatch('getUserCart')
        })
        .catch(err => {
          console.log(err)
        })
    },
    deleteTransactions({ dispatch }, transactionId) {
      axios({
        method: 'delete',
        url: `http://localhost:3000/transactions/${transactionId}`,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          console.log('success delete')

          dispatch('getUserCart')
        })
        .catch(err => {
          console.log(err)
        })
    }
  },
  modules: {}
})


1
Willkommen auf der Website. Es reicht nicht aus, nur ein Code-Snippet einzufügen. Bitte geben Sie einige Erklärungen zu Ihrem Code.
S.

0

Ich habe diesen Weg benutzt und es funktioniert:

store.js:

const state = {
  createSuccess: false
};

Mutationen.js

[mutations.CREATE_SUCCESS](state, payload) {
    state.createSuccess = payload;
}

action.js

async [mutations.STORE]({ commit }, payload) {
  try {
    let result = await axios.post('/api/admin/users', payload);
    commit(mutations.CREATE_SUCCESS, user);
  } catch (err) {
    console.log(err);
  }
}

getters.js

isSuccess: state => {
    return state.createSuccess
}

Und in Ihrer Komponente, in der Sie den Status aus dem Geschäft verwenden:

watch: {
    isSuccess(value) {
      if (value) {
        this.$notify({
          title: "Success",
          message: "Create user success",
          type: "success"
        });
      }
    }
  }

Wenn der Benutzer ein Formular sendet , wird die Aktion STORE aufgerufen. Nach dem erfolgreichen Erstellen wird anschließend die Mutation CREATE_SUCCESS festgeschrieben. Turn createSuccess ist true, und in der Komponente sieht der Watcher, dass sich der Wert geändert hat, und löst eine Benachrichtigung aus.

isSuccess sollte mit dem Namen übereinstimmen, den Sie in getters.js deklarieren

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.