vue.js 2 как посмотреть значения магазина из vuex

170

Я использую 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?

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

Ответы:

206

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

фруктово-кол-component.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>

Обратите внимание, что имя функции в watchобъекте должно совпадать с именем функции в computedобъекте. В приведенном выше примере имя count.

Новые и старые значения наблюдаемого свойства будут переданы в обратный вызов watch (функция count) в качестве параметров.

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

плодовые basket.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

Вы можете прочитать больше в следующих ресурсах:

Anastazy
источник
Мне просто интересно, что мне делать, когда watchдействие должно быть разделено на два шага: 1) во-первых, проверяется, кэшируются ли данные о желании и просто ли они возвращают кэшированные данные; 2) Если кеш вышел из строя, мне нужно асинхронное действие ajax для извлечения данных, но, похоже, это actionработа. Надеюсь, мой вопрос имеет смысл, спасибо!
1Cr18Ni9
В чем выгода от ответа micah5, который просто устанавливает наблюдателя в компоненте на стоимость магазина? У него меньше кода для поддержки.
Экзоцентрический
@ Exocentric Когда я написал ответ, вопрос был неясен для меня. Нет контекста, зачем нужно смотреть свойства. Думайте об этом как: «Я хочу смотреть переменную X, чтобы я мог сделать Y». Вероятно, именно поэтому большинство ответов предлагают столь разные подходы. Никто не знает, что это за намерения. Вот почему я включил «цели» в свой ответ. Если у вас разные цели, другой ответ может соответствовать им. Мой пример - только отправная точка для экспериментов. Это не предназначено, чтобы быть подключи и играй решение. Там нет "выгоды", потому что выгоды зависят от вашей ситуации.
Анастазий
@ 1Cr18Ni9 Я думаю, что кеширование не входит в код компонента. Вы закончите тем, что перепроектировали что-то, что должно быть действительно простым (выборка данных и привязка их к представлению). Кеширование уже реализовано в браузере. Вы можете воспользоваться этим, отправив правильные заголовки с сервера. Простое объяснение здесь: csswizardry.com/2019/03/cache-control-for-civilians . Вы также можете взглянуть на ServiceWorkers, которые позволяют веб-сайту работать даже без подключения к интернету.
Анастазий
61

Вы не должны использовать наблюдатели компонента для прослушивания изменений состояния. Я рекомендую вам использовать функции получения, а затем отобразить их внутри вашего компонента.

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
Получатель, вызванный один раз, получит только состояние в это время. Если вы хотите, чтобы это свойство отражало изменение состояния по сравнению с другим компонентом, вы должны просмотреть его.
С Тирни
3
Почему «Вы не должны использовать наблюдатели компонента для прослушивания изменений состояния»? Вот пример, о котором вы можете не подумать, если я захочу посмотреть токен из состояния и когда он изменится для перенаправления на другую страницу. Итак, в некоторых случаях вам нужно это сделать. Может быть, вам нужно больше опыта, чтобы знать это.
Шломи Леви
48

Это так просто, как:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}
micah5
источник
6
Это чертовски зрелище, более простое, чем любой из ответов здесь ... есть ли аргумент против этого?
Иниго
19
Это слишком просто, поэтому не похоже на js, js должно быть сложнее.
выезд
1
Было бы еще проще, если бы это былоfunction(n) { console.log(n); }
WofWca
2
Очень круто. Интересует также любой недостаток этого подхода. Пока что, похоже, работает хорошо.
namero999
1
Шутки в сторону. Это кажется намного лучше, чем принятый ответ, который требует дублирования имен функций в наблюдении и вычислении. Может ли эксперт прокомментировать, почему или почему бы не сделать это таким образом?
Экзоцентрический
43

Как уже упоминалось выше, не стоит смотреть изменения непосредственно в магазине.

Но в некоторых очень редких случаях это может быть полезно для кого-то, поэтому я оставлю этот ответ. Для других случаев, пожалуйста, смотрите ответ @ 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/en/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
      }
      );
yeahdixon
источник
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
dub
8

если вы используете машинопись, то вы можете:

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

..

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

Чжан Соль
источник
Почему именно это было отвергнуто? Просто потому, что решение для vue-class-component и TO запрашивало старые стили vue-class? Я считаю, что первое предпочтительнее. Может быть, @Zhang Sol мог бы упомянуть во введении, что это явно для vue-class-component?
JackLeEmmerdeur
Обратите внимание, почему декоратор машинописного текста предпочтительнее простого собственного решения vue: 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);
  }
 }
Mukundhan
источник
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 решением, предоставленным Gong . Или обращайтесь со своим компонентом осознанно и обновляйте, this.idкогда вам нужно обновить todoById.

Арсений-II
источник
Спасибо. Это именно тот случай, когда я использую, и действительно, геттер не может быть просмотрен тогда ...
pscheit
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измененном , которые не могут обрабатываться с помощью компьютерной mapState только там , где вы не можете сравнить newValueпротивoldValue
Januartha
@ Januartha, что ты предлагаешь к этой проблеме?
Биллаль Бегерадж
@ И да, конечно, это работает. Я просто хочу отметить, почему вы звоните store.dispatch? если вы хотите обработать store.stateизменения для store' why not handle it inside store.mutations`?
Januartha
@BillalBEGUERADJ Я предпочитаю решение dub более чистое
Januartha
@ Januartha, потому что перед мутацией может произойти вызов ajax, поэтому я использую его в 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

alloyking
источник
Вы можете запустить это в хуке beforeMount () вашего компонента, а затем отфильтровать входящие мутации с помощью оператора if. например, если (mutation.type == "names / SET_NAMES") {... сделать что-то}
Алехандро
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.

Rijosh
источник
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: {}
})

Semua Bisa
источник
1
Добро пожаловать на сайт. Ввод только фрагмента кода недостаточно. Пожалуйста, предоставьте некоторые пояснения по поводу вашего кода.
ПГК
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 true, и в компоненте наблюдатель увидит, что значение изменилось, и вызовет уведомление.

isSuccess должен совпадать с именем, которое вы объявляете в getters.js

Вуонг Тран
источник