vuejs 2如何从vuex中观察商店价值

raf*_*fff 129 javascript vue.js vuex vuejs2

我使用vuexvuejs 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;
  },
};
Run Code Online (Sandbox Code Playgroud)

我想知道是否有任何变化 my_state

我如何store.my_state在我的vuejs组件中观看?

Ana*_*azy 138

比方说,例如,你有一篮水果,每次你从篮子里添加或删除水果,你想(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, yaay!`)
    }
  }
}
</script>
Run Code Online (Sandbox Code Playgroud)

请注意,watch对象中函数的名称必须与对象中函数的名称匹配computed.在上面的例子中,名称是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
    }
  }
  // Obvously you would need some mutations and actions,
  // but to make example cleaner I'll skip this part.
})

export default basket
Run Code Online (Sandbox Code Playgroud)

您可以在以下资源中阅读更多内容:

  • 与 micah5 的答案(仅在组件中设置存储值的观察者)相比,这样做有什么好处?它需要维护的代码更少。 (2认同)

Gab*_*ert 60

您不应该使用组件的观察者来听取状态变化.我建议您使用getter函数,然后将它们映射到组件中.

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters({
      myState: 'getMyState'
    })
  }
}
Run Code Online (Sandbox Code Playgroud)

在你的商店:

const getters = {
  getMyState: state => state.my_state
}
Run Code Online (Sandbox Code Playgroud)

您应该能够通过this.myState在组件中使用来收听对商店所做的任何更改.

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

  • 这个答案是错的.他实际上需要观察计算属性. (54认同)
  • 一旦被调用,getter将只检索当时的状态.如果您希望该属性反映其他组件的状态更改,则必须观察它. (11认同)
  • 我不知道如何实现mapGetters。你能给我举个例子吗?这将是一个很大的帮助。我现在只是执行GONG的回答。泰 (2认同)
  • @Rbex“mapGetters”是“vuex”库的一部分。你不需要实施它。 (2认同)
  • 为什么“您不应该使用组件的观察程序来监听状态变化”?这是您可能没有想到的示例,如果我想从状态中监视令牌以及何时更改令牌以重定向到另一个页面。因此,在某些情况下您需要这样做。也许您需要更多的经验才能知道这一点。 (2认同)
  • 这还不够。在这种情况下你必须注意它。 (2认同)

GON*_*ONG 36

如上所述,直接在商店中观看更改并不是一个好主意

但在一些非常罕见的情况下,它可能对某人有用,所以我会留下这个答案.对于其他情况,请参阅@ 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.
    }
);
Run Code Online (Sandbox Code Playgroud)

更多细节:https://vuex.vuejs.org/en/api.html#vuex-store-instance-methods

  • @GabrielRobert我认为两者都有一席之地.如果您需要根据模板条件进行反应性更改,则使用带有mapState的计算值等是有意义的.但是否则,就像组件中的均匀流量控制一样,您需要一个完整的手表.你是对的,你不应该使用普通的组件观察者,而是状态.$ watch是为这些用例设计的 (14认同)
  • 每个人都提到它,但没有人说出原因!我正在尝试构建一个在更改时与数据库自动同步的vuex存储.我觉得商店里的观察者是最无摩擦的方式!你怎么看?仍然不是一个好主意? (12认同)
  • 我不认为直接观察州是个好主意.我们应该使用吸气剂.https://vuex.vuejs.org/en/getters.html#the-mapgetters-helper (3认同)

yea*_*xon 14

我认为提问者想要使用Vuex手表.

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

      },
      {
        deep:true
      }
      );
Run Code Online (Sandbox Code Playgroud)

  • 可以通过“this”访问 Vue 实例的某个地方。就像一个“创建的”钩子。基本上,您需要的任何组件 (2认同)

Jak*_*cki 11

如果你只想一个国家的财产,然后将组件内采取相应的行动,以该财产的变化,然后看下面的例子。

store.js

export const state = () => ({
 isClosed: false
})
export const mutations = {
 closeWindow(state, payload) {
  state.isClosed = payload
 }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我将创建一个boolean状态属性,我将在应用程序的不同位置对其进行更改,如下所示:

this.$store.commit('closeWindow', true)
Run Code Online (Sandbox Code Playgroud)

现在,如果我需要在其他组件中查看该状态属性,然后更改本地属性,我会在mounted钩子中写入以下内容:

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

首先,我在 state 属性上设置了一个观察者,然后在回调函数中我使用该value属性的 来更改localProperty.

我希望它有帮助!


小智 10

如果您使用打字稿,那么您可以:

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

..

@Watch("$store.state.something")
private watchSomething() {
   // use this.$store.state.something for access
   ...
}
Run Code Online (Sandbox Code Playgroud)


dub*_*ube 9

这适用于所有无法用吸气剂解决问题并真正需要观察者的人,例如与非第三方的东西交谈(请参阅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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果它只是关于组合本地和全局状态,mapState的doc也提供了一个例子:

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

  • 如果它是在文档中,它不是一个黑客,是吗?但是,它也不是vue/vuex的支持者 (2认同)

mic*_*ah5 9

就像这样简单:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 太简单了所以看起来不像js,js肯定更复杂。 (76认同)
  • 这是一个比这里任何答案都更直接的景象……有什么理由反对这样做吗……? (12认同)
  • 顺便说一句,如果商店是用模块命名的,只需写“$store.state.module.something” (4认同)
  • 超酷。也对这种方法的任何缺点感兴趣。到目前为止,它似乎运作良好。 (3认同)
  • 如果是`function(n) { console.log(n); 会更简单。}` (2认同)
  • 严重地。这似乎比接受的答案要好得多,后者需要在监视和计算中重复函数名称。专家能否评论一下为什么或为什么不这样做? (2认同)
  • 有趣的是,我首先写了“this.$store.state.something”,但它不起作用,所以我认为这不起作用......不知道你需要省略“this”部分!有人可以解释一下吗?我想观察者已经假设您正在处理 Vue 实例,所以“this”是不必要的? (2认同)
  • @seyet只使用'$store.state.fileOne.fileTwo.fileThree.stateFile.yourState' (2认同)

Ado*_*zis 7

我真的尝试了一切来让它工作。

理论

我发现由于某种原因,对对象的更改$store不一定会触发.watch方法。我的解决方法是

  • 店铺
    • 创建一个复杂的数据集,它应该将更改传播到组件
    • 建立在递增计数器state作为一个标志,看时,一个组件传播更改
    • 创建一个方法$store.mutators来改变复杂的数据集并增加计数器标志
  • 成分
    • 注意$store.state标志的变化。当检测到变化时,从$store.state复杂的数据集中更新本地相关的反应性变化
    • $store.state使用我们的$store.mutators方法更改的数据集

执行

这是这样实现的:

店铺

let store = Vuex.Store({
  state: {
    counter: 0,
    data: { someKey: 0 }
  },
  mutations: {
    updateSomeKey(state, value) {
      update the state.data.someKey = value;
      state.counter++;
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

成分

  data: {
    dataFromStoreDataSomeKey: null,
    someLocalValue: 1
  },
  watch: {
    '$store.state.counter': {
        immediate: true,
        handler() {
           // update locally relevant data
           this.someLocalValue = this.$store.state.data.someKey;
        }
     }
  },
  methods: {
    updateSomeKeyInStore() { 
       this.$store.commit('updateSomeKey', someLocalValue);
  }
Run Code Online (Sandbox Code Playgroud)

可运行的演示

这很复杂,但基本上在这里我们正在观察标志的变化,然后更新本地数据以反映存储在 $state

Vue.config.devtools = false

const store = new Vuex.Store({
  state: {
    voteCounter: 0,
    // changes to objectData trigger a watch when keys are added,
    // but not when values are modified?
    votes: {
      'people': 0,
      'companies': 0,
      'total': 0,
    },
  },
  mutations: {
    vote(state, position) {
      state.votes[position]++;
      state.voteCounter++;
    }
  },
});


app = new Vue({
  el: '#app',
  store: store,
  data: {
    votesForPeople: null,
    votesForCompanies: null,
    pendingVote: null,
  },
  computed: {
    totalVotes() {
      return this.votesForPeople + this.votesForCompanies
    },
    peoplePercent() {
      if (this.totalVotes > 0) {
        return 100 * this.votesForPeople / this.totalVotes
      } else {
        return 0
      }
    },
    companiesPercent() {
      if (this.totalVotes > 0) {
        return 100 * this.votesForCompanies / this.totalVotes
      } else {
        return 0
      }
    },
  },
  watch: {
    '$store.state.voteCounter': {
        immediate: true,
        handler() {
          // clone relevant data locally
          this.votesForPeople = this.$store.state.votes.people
          this.votesForCompanies = this.$store.state.votes.companies
        }
     }
  },
  methods: {
    vote(event) {
      if (this.pendingVote) {
        this.$store.commit('vote', this.pendingVote)
      }
    }
  }
  
})
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script src="https://unpkg.com/vuex@3.5.1/dist/vuex.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">


<div id="app">
   <form @submit.prevent="vote($event)">
      <div class="form-check">
         <input
           class="form-check-input" 
           type="radio" 
           name="vote" 
           id="voteCorps"
           value="companies"
           v-model="pendingVote"
          >
         <label class="form-check-label" for="voteCorps">
         Equal rights for companies
         </label>
      </div>
      <div class="form-check">
         <input
           class="form-check-input" 
           type="radio" 
           name="vote"
           id="votePeople" 
           value="people"
           v-model="pendingVote"
         >
         <label class="form-check-label" for="votePeople">
         Equal rights for people
         </label>
      </div>
      <button
        class="btn btn-primary"
        :disabled="pendingVote==null"
      >Vote</button>
   </form>
   <div
     class="progress mt-2"
     v-if="totalVotes > 0"
    >
      <div class="progress-bar"
        role="progressbar"
        aria-valuemin="0"
        :style="'width: ' + peoplePercent + '%'"
        :aria-aluenow="votesForPeople"
        :aria-valuemax="totalVotes"
      >People</div>
      <div
        class="progress-bar bg-success"
        role="progressbar"
        aria-valuemin="0"
        :style="'width: ' + companiesPercent + '%'"
        :aria-valuenow="votesForCompanies"
        :aria-valuemax="totalVotes"
      >Companies</div>
   </div>
</div>
Run Code Online (Sandbox Code Playgroud)


Ami*_*mir 7

在计算中使用你的吸气剂然后观察它并做你需要的事情

    computed:{
    ...mapGetters(["yourGetterName"])
 },
 watch: {
    yourGetterName(value) {
       // Do something you need
    },

  }
Run Code Online (Sandbox Code Playgroud)


Ars*_*-II 6

观察商店变化的最好方法是使用mapGettersGabriel 所说的。但是有一种情况,你不能通过mapGetters例如你想使用参数从商店获取一些东西:

getters: {
  getTodoById: (state, getters) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您不能使用mapGetters. 你可以尝试做这样的事情:

computed: {
    todoById() {
        return this.$store.getters.getTodoById(this.id)
    }
}
Run Code Online (Sandbox Code Playgroud)

但不幸的todoById 是只有在this.id更改时才会更新

如果您希望在这种情况下更新组件,请使用this.$store.watch 龚提供的解决方案。或者有意识地处理你的组件并this.id在需要更新时更新todoById


小智 6

在组件内部,创建一个计算函数

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

现在可以查看计算出的函数名称,例如

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

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
  }
}
Run Code Online (Sandbox Code Playgroud)

这将监视 store 中的所有嵌套值my_state


Ami*_*IRI 5

您可以使用 Vuex actionsgetter计算属性观察者的组合来监听 Vuex 状态值的变化。

HTML 代码:

<div id="app" :style='style'>
  <input v-model='computedColor' type="text" placeholder='Background Color'>
</div>
Run Code Online (Sandbox Code Playgroud)

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()}`)
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

请参阅 JSFiddle 演示


Muk*_*han 5

通过观察和设置值更改来创建商店变量的Local状态。这样,表单输入v模型的局部变量更改不会直接更改存储变量

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


all*_*ing 5

You could also subscribe to the store mutations:

store.subscribe((mutation, state) => {
  console.log(mutation.type)
  console.log(mutation.payload)
})
Run Code Online (Sandbox Code Playgroud)

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