首页 > 解决方案 > 如何在 Vuex 中设置计时器?

问题描述

我刚开始学习 Vue 和 Vuex。我正在尝试设置一个倒数计时器,当它被点击时,它从 5 秒开始计数。逻辑已放置在 Vuex 存储中以进行状态管理,但我无法让计时器运行(它卡在 5 不变)。

在父母(Home.vue)

<template>
  <div class="home">
    <Header 
    @change-game-button="changeGameButton"
     :gameInActive="gameInActive"
     :gameActive="gameActive"
    />
  </div>
</template>

<script>
import Header from '../components/Header'

export default {
  name: 'Home',
  components: {
    Header,
  },
  data() {
    return {
      gameInActive: true,
      gameActive: false
    }
  },
  methods: {
    changeGameButton() {
      console.log("Game button fired");
      this.gameInActive = !this.gameInActive;
      this.gameActive = !this.gameActive;
    }
  }
}
</script>

在组件(Header.vue)

<template>
  <div class="header-container">
    <StartResetButton 
    @btn-click="$emit('change-game-button')"
    :buttonText="gameInActive ? 'Start' : 'Restart'"
    :buttonColor="gameInActive ? '#90ee90' : '#FED8B1'"
    @click="gameInActive? 'resetCounter' : 'startCounter'"
    />
  </div>
  <div class="initialInstruction" v-if="gameInActive">{{ initialInstruction }}</div>
  <div class="gameStartTimer" v-if="gameActive">{{ gameStartTimer }}</div>
</template>

<script>
import StartResetButton from "./StartResetButton";
import { mapActions, mapMutations, mapState } from "vuex";

export default {
  components: {
    StartResetButton,
  },
  props: {
      gameInActive: Boolean,
      gameActive: Boolean,
  },
  computed: {
      ...mapState(["gameStartTimer", "initialInstruction"])
  },
  methods: {
    //   ...mapMutations(["countDown"]),
      ...mapActions(["startCounter", "resetCounter"])
  },
  emits: ['change-game-button']
};
</script>

在商店 (index.js)

import { createStore } from 'vuex'

export default createStore({
  state: {
    gameStartTimer: 5,
    initialInstruction: "Press Start to Begin"
  },
  mutations: {
    stopCounter(state) {
      state.gameStartTimer = 5
    },
    counter(state) {
      if (state.gameStartTimer > 0) {
        setTimeout(() => {
          state.gameStartTimer--;
        }, 1000)
      }
    }
  },
  actions: {
    startCounter(context) {
      context.commit('counter')
    },
    resetCounter(context) {
      context.commit('stopCounter')
    }
  },
  modules: {
  }
})

StartResetButton.vue

<template>
  <button 
  class="btn"
  @click="onClick()" 
  :style="{ background: buttonColor }">
    {{ buttonText }}
  </button>
</template>

<script>
export default {
  name: "StartResetButton",
  props: {
    buttonText: String,
    buttonColor: String,
  },
  methods: {
      onClick() {
          this.$emit('btn-click')
      }
  }
};
</script>

关于为什么计时器不会触发的任何提示?

标签: javascripthtmlvue.jsvuex

解决方案


问题是这不会执行点击处理程序:

@click="gameInActive? 'resetCounter' : 'startCounter'"

v-on( @) value 应该是用作事件处理程序的函数,或者是对事件进行评估的表达式。此表达式不会导致resetCounter, 等函数调用,因为typeof (this.gameInActive? 'resetCounter' : 'startCounter') === 'string'. 最好提供一个单独的处理程序来做出决定,而不是将条件放入模板中。

Vuex 支持 Promise,最好使用 Promise 控制流进行异步操作。setTimeout回调执行一次。如果它应该多次递减,setTimeout则应在循环中使用await,或setInterval应改为使用。

突变是直接访问状态的同步且通常细粒度的函数。可以有称为decrementCount,startCounter等的。

  state: {
    count: 5,
    counter: false
  },
  actions: {
    async startCounter({ state }) {
      state.counter = true;
      while (state.count > 0 && state.counter) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        if (state.counter)
          state.count--;
      }
      state.counter = false;
    },
    resetCounter(context) {
      state.count = 5;
      state.counter = false;
    }
  },

推荐阅读