vue.js 2, як дивитися значення магазину з vuex


168

Я використовую vuexі vuejs 2разом.

Я новачок vuex, хочу спостерігати за storeзміною змін.

Я хочу додати watchфункцію в своюvue component

Ось що я маю досі:

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

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

Я хочу знати, чи є якісь зміни в my_state

Як я дивлюся store.my_stateв своєму компоненті vuejs?


використовуйте плагін Vuejs, який поставляється з хромом, це стане в нагоді
AKASH PANDEY

Відповіді:


206

Скажімо, наприклад, що у вас є кошик з фруктами, і кожного разу, коли ви додаєте або виймаєте фрукти з кошика, ви хочете (1) відобразити інформацію про кількість фруктів, але ви також (2) хочете отримувати повідомлення про підрахунок плодів у якийсь фантазійний спосіб ...

фрукти-кол-компонент.вью

<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>

Зверніть увагу, що ім'я функції в watchоб'єкті повинно відповідати імені функції в computedоб'єкті. У прикладі вище назва count.

Нові та старі значення властивості, що спостерігається, будуть передані у зворотний виклик годинника (функція підрахунку) як параметри.

Магазин кошиків може виглядати так:

фрукти-кошик.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

Ви можете прочитати більше в наступних ресурсах:


Мені просто цікаво, що я watchповинен робити, коли дію слід розділити на два етапи: 1) По-перше, перевірити, чи є кешовані дані бажання та чи просто повертає кешовані дані; 2) Якщо кеш не вдався, для отримання даних мені потрібна дія асинхронного ajax, але, здається, це actionробота. Сподіваючись, моє запитання має сенс, дякую!
1Cr18Ni9

Яка користь від цього у відповіді micah5, яка просто встановлює спостерігача в компоненті, на значення магазину? У ньому менше коду для підтримки.
Екзоцентричний

@Exocentric Коли я написав відповідь, питання мені не було зрозумілим. Немає контексту, чому потрібно дивитися властивості. Подумайте про це так: "Я хочу переглянути змінну X, щоб я міг зробити Y". Можливо, саме тому більшість відповідей пропонують настільки різні підходи. Ніхто не знає, який умисел. Ось чому я включив у свою відповідь «цілі». Якщо у вас різні цілі, різні відповіді можуть відповідати їм. Мій приклад - лише відправна точка для експериментів. Це не розроблено як підключення та гра. Немає «вигоди», оскільки вигода залежить від вашої ситуації.
Анастазі

@ 1Cr18Ni9 Я думаю, що кешування не належить до коду компонента. Ви в кінцевому підсумку перепрофілюєте щось, що повинно бути справді простим (отримання даних та прив'язка їх до перегляду). Кешування вже реалізовано в браузері. Ви можете скористатися цим, відправивши правильні заголовки з сервера. Просте пояснення тут: csswizardry.com/2019/03/cache-control-for-civilians . Ви також можете подивитися на ServiceWorkers, які дозволяють веб-сайту працювати навіть без підключення до Інтернету.
Анастазі

61

Не слід використовувати спостережники компонентів для прослуховування змін у стані. Я рекомендую вам скористатися функціями getters, а потім зіставити їх всередині вашого компонента.

import { mapGetters } from 'vuex'

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

У вашому магазині:

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

Ви повинні мати можливість слухати будь-які зміни, внесені у ваш магазин, використовуючи this.myStateу своєму компоненті.

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


1
Я не знаю, як реалізувати MapGetters. Чи можете ви вказати мені на приклад. Це було б великою допомогою. Я просто реалізую відповідь GONG на даний момент. TY
Rbex

1
@Rbex "mapGetters" є частиною бібліотеки "vuex". Вам не потрібно це реалізовувати.
Габріель Роберт

69
Ця відповідь просто неправильна. Йому фактично потрібно спостерігати за обчислюваними властивостями.
Хуан

15
Колись зателефонований геттер буде отримувати стан лише на той час. Якщо ви хочете, щоб ця властивість відображала зміну стану з іншого компонента, ви повинні його переглянути.
C Tierney

3
Чому "Ви не повинні використовувати спостерігачі компонентів для прослуховування змін стану"? Ось приклад, про який ви можете не думати, якщо я хочу дивитися на маркер із стану, а коли він змінюється, щоб перенаправляти на іншу сторінку. значить, вам потрібно це зробити. можливо, вам знадобиться більше досвіду, щоб це знати.
Шломі Леві

47

Це так просто, як:

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

6
Це прокляте видовище більш просте, ніж будь-яка відповідь тут ... чи є аргументи проти цього ..?
Ініго

19
Це занадто просто, щоб не схожий на js, js повинен бути складнішим.
розвідка

1
Було б ще простіше, якби це булоfunction(n) { console.log(n); }
WofWca

2
Супер круто. Цікавить також будь-який недолік такого підходу. Поки це, здається, працює добре.
namero999

1
Серйозно. Це здається набагато кращим, ніж прийнята відповідь, яка вимагає дублювання імен функцій у вахті та обчисленні. Чи може експерт прокоментувати, чому або чому не робити цього таким чином?
Екзоцентричний

42

Як було сказано вище, не годиться спостерігати за змінами безпосередньо в магазині

Але в деяких дуже рідкісних випадках комусь це може бути корисно, тому я залишу цю відповідь. Для інших випадків див. Відповідь @ gabriel-robert

Це можна зробити через state.$watch. Додайте це у свій created(або там, де вам це потрібно виконати) метод у компоненті

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.
    }
);

Детальніше: https://vuex.vuejs.org/api/#watch


3
Я не думаю, що це гарна ідея дивитися стан безпосередньо. Ми повинні використовувати геттери. vuex.vuejs.org/uk/getters.html#the-mapgetters-helper
Габріель Роберт

14
@GabrielRobert Я думаю, що є місце для обох. Якщо вам потрібно реально змінити умови шаблону на основі, обчислюване значення з mapState тощо має сенс. Але в іншому випадку, як для рівномірного контролю потоку в компоненті, вам потрібен повний годинник. Ви маєте рацію, ви не повинні користуватися простими компонентами, але штат. $ Watch призначений для таких випадків використання
roberto tomás

14
Всі це згадують, але ніхто не каже чому! Я намагаюся створити магазин vuex, який автоматично синхронізується з БД при змінах. Я відчуваю, що спостерігачі в магазині - це самий спосіб без тертя! Що ти думаєш? Ще не гарна ідея?
mesqueeb

16

Я думаю, що запитувач хоче використовувати годинник з Vuex.

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

      },
      {
        deep:true
      }
      );

13

Це для всіх людей, які не можуть вирішити свою проблему з дітьми, і насправді дуже потрібен спостерігач, наприклад, щоб поспілкуватися з сторонніми сторонніми матеріалами (див. Vue Watchers про те, коли користуватися спостерігачами).

Оглядачі та обчислені значення компонента Vue також працюють на обчислені значення. Так що з vuex це не відрізняється:

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);
        }
    }
}

якщо мова йде лише про поєднання локального та глобального стану, документ mapState також надає приклад:

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

приємний хак, але занадто виснажливий, ти не думаєш?
Martian2049

2
Це не хак, якщо він є в документах, чи не так? Але тоді це не є аргументом для vue / vuex
дуб

8

якщо ви використовуєте машинопис, ви можете:

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

..

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


Чому саме це було знято? Просто тому, що рішення призначене для vue-class-компонента, а TO запитував старі стилі vue-class? Я вважаю колишнього кращим. Можливо, @Zhang Sol міг би згадати у вступі, що це явно для vue-class-компонента?
JackLeEmmerdeur

Примітка впевнений , чому машинопис декоратор б краще вю рідне рішення так просто , як це: stackoverflow.com/a/56461539/3652783
yann_yinn

6

Створіть локальну змінну вашого магазину, переглянувши та встановивши зміни цінності. Таким чином, що зміна локальної змінної для v-моделі форм-вводу не мутує безпосередньо змінну сховища .

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

5

Найкращий спосіб спостерігати за змінами магазину - це використовувати, mapGettersяк сказав Габріель. Але є випадок, коли ви не можете це зробити, mapGettersнаприклад, ви хочете отримати щось із магазину за допомогою параметра:

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

у такому випадку ви не можете користуватися mapGetters. Ви можете спробувати зробити щось подібне замість цього:

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

Але, на жаль, todoById буде оновлюватися, лише якщо this.idбуде змінено

Якщо ви хочете оновити компоненти в такому випадку, використовуйте this.$store.watch рішення, надане Гонгом . Або обробляйте свій компонент свідомо та оновлюйте, this.idколи вам потрібно оновити todoById.


спасибі. Саме такий мій випадок використання, і справді за
геттером

5

Якщо ви просто хочете подивитися на державну власність , а потім діяти в рамках компонента в відповідно до зміни цієї властивості тоді дивіться приклад нижче.

В store.js:

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

У цьому сценарії я створюю booleanдержавну власність, яку я хочу змінити в різних місцях програми так:

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

Тепер, якщо мені потрібно переглянути цю власність у якомусь іншому компоненті, а потім змінити локальну власність, я напишу наступне на mountedгачку:

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

По-перше, я встановлюю спостерігач на властивість стану, а потім у функції зворотного дзвінка використовую valueцю властивість для зміни localProperty.

Я сподіваюся, що це допомагає!


3

Коли ви хочете дивитися на державному рівні, це можна зробити так:

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

Недоцільно обробляти store.stateзміни шляхом dispatchдії стану від компонента, оскільки така поведінка працює лише тоді, коли ви використовуєте цей компонент. Також ви могли закінчитися нескінченним циклом. Слідкуйте за тим, щоб store.stateзмінити рідко, наприклад, якщо у вас є компонент або сторінка, яка повинна виконати певні дії на основі store.stateзмін, які не вдалося впоратися, використовуючи обчислену картуState тільки там, де ви не можете порівняти newValueпорівняноoldValue
січня 18.01.18

@Januartha, що ви тоді пропонуєте до цієї проблеми?
Біллал Бегерадж

@Andy так, звичайно, його робота. Я просто хочу зазначити, чому ти дзвониш store.dispatch? якщо ви хочете обробити store.stateзміни для store' why not handle it inside store.mutations`?
січня

@BillalBEGUERADJ я prever Дуб рішення є більш чистіше
Januartha

@Januartha, тому що перед тим, як зробити мутацію, може відбутися дзвінок в Аяксі, тому я використовую store.dispatchспочатку Наприклад, я хочу отримувати всі міста з якоїсь країни, коли $store.state.countryзмінюється, тому додаю це до спостерігача. Тоді я б написав дзвінок в Ajax: в store.dispatch('fetchCities')я пишу:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Енді,

2

Ви можете використовувати комбінацію Vuex дій , видобувачів , обчислених властивості і спостерігачів слухати зміни на державному значенні Vuex.

HTML-код:

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

JavaScript-код:

'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()}`)
    }
  }
})

Див. Демонстрацію JSFiddle .


1

Ви також можете підписатися на мутації магазину:

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

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


Ви можете запустити це в гачок преди компонента компонента beforeMount (), а потім відфільтрувати вхідні мутації за допомогою оператора if. Наприклад, якщо (mutation.type == "імена / SET_NAMES") {... робити щось}
Alejandro

1

Всередині компонента створіть обчислену функцію

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

Тепер назву обчисленої функції можна спостерігати, як

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

Зміни, внесені в vuexстан my_state, відобразяться в обчислюваній функції myStateі запускають функцію годинника.

Якщо в штаті my_stateє вкладені дані, то цей handlerваріант допоможе більше

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

Це дозволить переглянути всі вкладені значення в магазині my_state.


0

Ви також можете використовувати mapState у своєму компоненті vue, щоб напряму отримувати стан з магазину.

У вашому компоненті:

computed: mapState([
  'my_state'
])

Де my_stateє змінна від магазину.


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
Ласкаво просимо на сайт. Поставити лише фрагмент коду недостатньо. Будь ласка, надайте пояснення щодо свого коду.
pgk

0

Я використовував цей спосіб, і це працює:

store.js:

const state = {
  createSuccess: false
};

mutations.js

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

Actions.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
}

І у своєму компоненті, де ви використовуєте стан від магазину:

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

Коли користувач подає форму, дія STORE буде викликана , після створеного успіху після цього здійснюється мутація CREATE_SUCCESS . Поворот createSuccess є істинним, і в компоненті спостерігач побачить, що значення змінилося і запустить сповіщення.

isSuccess повинен відповідати імені, яке ви заявляєте в getters.js

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.