Vue 3.0教程(for beginner):反应计时器游戏

1,742 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

本章节中,我们使用vue 3.0从零开发一个反应计时器小游戏。

创建App

参考前面的教程Vue 3.0教程(for beginner):Vue Cli(上篇),基于vue-cli创建一个reaction-timer项目应用:

image.png

删除HelloWorld组件并将App.vue组件内容替换为:

<template>
  <h1>A reaction timer game.</h1>
</template>

image.png

一个「开始」

首先新增一个Block.vue组件,用来画一个矩形方块:

<template>
  <div class="block">捶我🐶</div>
</template>

<script>
export default {
  name: 'Block',
}
</script>

然后在App.vue组件内添加一个Play按钮,并引入Block组件:

<template>
  <h1>A reaction timer game.</h1>
  <button @click="play">开始</button>
  <Block v-if="isPlaying" />
</template>

<script>
import Block from './components/Block.vue'
export default {
  name: 'App',
  components: { Block },
  data() {
    return {
      isPlaying: false,
      delay: null,
    }
  },
  methods: {
    play() {
      this.delay = 2000 + Math.random() * 5000
      this.isPlaying = true
    },
  },
}
</script>

play方法的作用:

  • 控制游戏开始状态isPlaying
  • 产生一个随机延时,单位ms 点击按钮之后便会显示Block方块:

image.png

实践中我们发现,点击「开始」按钮之后,Block方块直接显示,而我们需要它做一个延时。另外,该按钮仍可继续点击,这并不是我们想要的,我们需要在进入游戏状态时禁用该按钮:

<template>
  <h1>A reaction timer game.</h1>
  <button @click="play" :disabled="isPlaying">开始</button>
  <Block v-if="isPlaying" :delay="delay" />
</template>

这里,我们把产生的delay作为参数传递给Block组件,组件内该如何使用呢?

生命周期&钩子函数

下图展示了一个vue实例的生命周期,我们不需要马上完全弄明白它们,我们会在之后的章节应用中深入地了解这些。

image.png 在vue应用里面,每一个vue组件都有相同的生命周期:

  • beforeCreate:vue组件实例还未完全创建,处在最初的初始化阶段,不能访问组件实例中的data数据和template中的don元素;
  • created:组件实例已经创建完成,但还未挂在到dom上,可以访问data中的数据,但还未能访问dom元素;
  • beforeMounttemplate模板编译完成,dom挂载之前,可以访问data中的数据,但还未能访问dom元素;
  • mounted:完成组件挂载,一般我们会在这个阶段发起一些异步的数据请求(当然在createdbeforeMount阶段也是可以的);
  • beforeUpdate:组件挂载之后发生数据变更,组件render之前(最新的数据还未反应在浏览器页面)触发;
  • updated:组件render之后触发;
  • beforeDestroy:组件卸载之前,一般我们会在这个阶段做事件注销,定时器清除等操作;
  • destoryed:组件卸载之后;

我们给Block.vue组件加上钩子函数:

<template>
  <div v-if="isShow" class="block" @click="close">捶我🐶</div>
</template>

<script>
export default {
  name: 'Block',
  props: ['delay'],
  data() {
    return {
      isShow: false,
    }
  },
  mounted() {
    console.log('>> mounted.')
    setTimeout(() => {
      this.isShow = true
    }, this.delay)
  },
  updated() {
    console.log('>> updated.')
  },
  methods: {
    close() {
      this.isShow = false
    },
  },
}
</script>

hook-01.gif

创建计时器

当我们点击开始按钮之后,会等待一段随机时间,知道出现绿色方块,玩家在看到绿色方块之后,需要尽快地点击方块。那么,我们需要一个计时器来测量玩家的这个反应时间。

我们给Block新增两个函数startTimerstopTimer

<template>
  <div v-if="isShow" class="block" @click="close">捶我🐶</div>
</template>

<script>
export default {
  name: 'Block',
  props: ['delay'],
  data() {
    return {
      isShow: false,
      timer: null,
      reactionTime: 0,
    }
  },
  mounted() {
    setTimeout(() => {
      this.isShow = true
      this.startTimer()
    }, this.delay)
  },
  methods: {
    startTimer() {
      this.timer = setInterval(() => {
        this.reactionTime += 10
      }, 10)
    },
    stopTimer() {
      clearInterval(this.timer)
      console.log(this.reactionTime)
    },
    close() {
      this.isShow = false
      this.stopTimer()
    },
  },
}
</script>

利用setInterval在绿色方块出现之时开始计时,在用户点击方块之后停止计时,统计期间的反应耗时。

reaction-timer-01.gif

自定义事件

我们在Block组件中统计到了耗时,需要将这个数据传递给父组件,在父组件中显示出来。

<template>
  <div v-if="isShow" class="block" @click="close">捶我🐶</div>
</template>

<script>
export default {
  name: 'Block',
  props: ['delay'],
  emits: ['end'],
  data() {
    return {
      isShow: false,
      timer: null,
      reactionTime: 0,
    }
  },
  mounted() {
    setTimeout(() => {
      this.isShow = true
      this.startTimer()
    }, this.delay)
  },
  methods: {
    startTimer() {
      this.timer = setInterval(() => {
        this.reactionTime += 10
      }, 10)
    },
    stopTimer() {
      clearInterval(this.timer)
    },
    close() {
      this.isShow = false
      this.stopTimer()
      this.$emit('end', this.reactionTime)
    },
  },
}
</script>

通过emits定义了end的自定义事件,并将reactionTime作为参数传递进去,然后在App.vue中获取显示:

<template>
  <h1>A reaction timer game.</h1>
  <button @click="play" :disabled="isPlaying">开始</button>
  <Block v-if="isPlaying" :delay="delay" @end="endGame" />
  <p v-show="showScore">您的反应时间:{{ score }}ms</p>
</template>

<script>
import Block from './components/Block.vue'
export default {
  name: 'App',
  components: { Block },
  data() {
    return {
      isPlaying: false,
      delay: null,
      showScore: false,
      score: null,
    }
  },
  methods: {
    play() {
      this.delay = 2000 + Math.random() * 3000
      this.isPlaying = true
      this.showScore = false
    },
    endGame(score) {
      this.score = score
      this.isPlaying = false
      this.showScore = true
    },
  },
}
</script>

reaction-timer-02.gif

未完待续Vue 3.0教程(for beginner):Forms&Inputs