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?


используйте плагин 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

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


Мне просто интересно, что мне делать, когда 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)
  }
}

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

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

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.


Спасибо. Это именно тот случай, когда я использую, и действительно, геттер не может быть просмотрен тогда ...
— 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


Вы можете запустить это в хуке 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.


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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.