持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
本章节中,我们使用vue 3.0从零开发一个反应计时器小游戏。
创建App
参考前面的教程Vue 3.0教程(for beginner):Vue Cli(上篇),基于vue-cli创建一个reaction-timer项目应用:
删除HelloWorld组件并将App.vue组件内容替换为:
<template>
<h1>A reaction timer game.</h1>
</template>
一个「开始」
首先新增一个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方块:
实践中我们发现,点击「开始」按钮之后,Block方块直接显示,而我们需要它做一个延时。另外,该按钮仍可继续点击,这并不是我们想要的,我们需要在进入游戏状态时禁用该按钮:
<template>
<h1>A reaction timer game.</h1>
<button @click="play" :disabled="isPlaying">开始</button>
<Block v-if="isPlaying" :delay="delay" />
</template>
这里,我们把产生的delay作为参数传递给Block组件,组件内该如何使用呢?
生命周期&钩子函数
下图展示了一个vue实例的生命周期,我们不需要马上完全弄明白它们,我们会在之后的章节应用中深入地了解这些。
在vue应用里面,每一个vue组件都有相同的生命周期:
- beforeCreate:vue组件实例还未完全创建,处在最初的初始化阶段,不能访问组件实例中的
data数据和template中的don元素; - created:组件实例已经创建完成,但还未挂在到dom上,可以访问
data中的数据,但还未能访问dom元素; - beforeMount:
template模板编译完成,dom挂载之前,可以访问data中的数据,但还未能访问dom元素; - mounted:完成组件挂载,一般我们会在这个阶段发起一些异步的数据请求(当然在
created和beforeMount阶段也是可以的); - 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>
创建计时器
当我们点击开始按钮之后,会等待一段随机时间,知道出现绿色方块,玩家在看到绿色方块之后,需要尽快地点击方块。那么,我们需要一个计时器来测量玩家的这个反应时间。
我们给Block新增两个函数startTimer和stopTimer:
<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在绿色方块出现之时开始计时,在用户点击方块之后停止计时,统计期间的反应耗时。
自定义事件
我们在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>