3×3连子棋(也叫做井字棋)其实还并不算复杂,3×3的棋盘最多可以放9个棋子,一旦有一方连成三子,则此局棋结束。现使用Vue.js来使用Vue CLI搭建井字棋项目,并将其发布到github上
一:需要用到的工具
Node.js、Vue CLI、VScode
二:项目搭建
1. 搭建好项目原型后,将项目拖入VScode中:
项目初始结构如下:2. 打开src
目录下的App.vue
,清空里面的内容,只保留如下结构,我们可以在标签template
中写html
结构,script
标签中写 js
代码,在style
标签中写css
代码
<template>
</template>>
<script>
export default {
}
</script>
<style>
</style>
3. 在src
目录下新建一个Cell.vue
,创建井字格组件
实现的功能:点击井字格,切换格子里的棋子x
和0
主要点:条件渲染v-if
,v-else
;事件处理v-on
,简写为@
<template>
<div class="cell" @click="change">
<template v-if="a">x</template>
<template v-else>o</template>
</div>
</template>
<script>
export default {
data(){
return {
a: false
}
},
methods:{
change(){
this.a = !this.a
}
}
}
</script>
4. 将Cell.vue
引入App.vue
中,打开App.vue
,
主要点:在 模块系统中局部注册组件,import xx from './xx'
,components{xx}
此时,我们就可以在App模版中使用Cell组件了
<template>
<Cell></Cell>
</template>
<script>
import Cell from './Cell'
export default {
components:{Cell}
}
</script>
5. 创建完整的井字棋结构,3*3结构
坑:不能直接在template
中直接复用多个Cell
,会报Component template should contain exactly one root element
这样的错误,此时我们应该用一个元素将他们包裹起来
//错误写法
<template>
<Cell></Cell>
<Cell></Cell>
<Cell></Cell>
<Cell></Cell>
</template>
//正确写法
<template>
<div class="container">
<div class="row">
<Cell />
<Cell />
<Cell />
</div>
...
</div>
</template>
...
此时页面展示的如下图6. 我们希望点击的时候再出现o
或x
实现思路:在 App
中设置一个参数n,每点击一次格子则n
加1,再将n传到组件Cell
中,判断此时n
的值为奇数还是偶数,偶数时格子中显示x
,奇数时显示o
主要点:父子组件中传递参数,
父组件 to 子组件:父组件v-bind:n="n"
,@click="cellClick"
子组件:props:['n']
子组件 to 父组件:子组件:this.$emit('xxx',data)
,父组件:<Cell @click="cellClick($event)" />
// App.vue
<template>
<div class="container">
<div class="row">
<Cell :n="n" @click="cellClick" />
<Cell :n="n" @click="cellClick" />
<Cell :n="n" @click="cellClick" />
</div>
...
</div>
</template>
<script>
import Cell from './Cell'
export default {
components:{Cell},
data(){
return {
n:0
}
},
methods: {
cellClick(){
this.n += 1
}
}
}
</script>
//Cell.vue
<template>
<div class="cell" @click="change">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</template>
<script>
export default {
props:['n'],
data(){
return {
a: false,
text:''
}
},
methods:{
change(){
this.a = true
if(this.text !== '') return //点击过的格子不能再点击
// console.log(this.n)
this.text = this.n % 2 === 0? 'x':'o'
this.$emit('click')
}
}
}
</script>
此时,大概完成了井字棋的雏型,点击格子轮流切换棋子
7. 判断输赢
思路:将点击过的格子里的值用3x3的数组记录下来,再判断数组相关值是否相等,相等则游戏结束。游戏结束,则重新渲染页面,开始新一轮游戏
主要点:1. 子组件向父组件传递参数 2. 游戏胜利的条件判断 3. 游戏结束后如何重新渲染页面this.$nextTick()
//App.vue
<template>
<div class="container">
<div>第{{n}}手</div>
<div class="row">
<Cell :n="n" @click="cellClick(0,$event)" v-if="update"/>
...
</div>
...
</template>
<script>
import Cell from './Cell'
export default {
components:{Cell},
data(){
return {
n:0,
result:'',
update:true, //用来判断组件页面重新渲染与否
isend: false, //用来判断是游戏是否结束
map:[[null,null,null],[null,null,null],[null,null,null]]
}
},
methods: {
cellClick(i,text){
this.n += 1
this.map[Math.floor(i/3)][i%3] = text
this.win()
this.isended()
},
win(){
...
},
isended(){
...
}
},
reload() {
...
}
}
</script>
8. 将项目部署到 github上
- 手动推送更新
在 vue.config.js 中设置正确的 publicPath。 - 如果打算将项目部署到
https://<USERNAME>.github.io/
上,publicPath
将默认被设为"/"
,你可以忽略这个参数。 - 如果打算将项目部署到
https://<USERNAME>.github.io/<REPO>/
上 (即仓库地址为https://github.com/<USERNAME>/<REPO>
),可将publicPath
设为"/<REPO>/"
。举个例子,如果仓库名字为“my-project”,那么vue.config.js
的内容应如下所示:
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/my-project/'
: '/'
}
9. 具体部署
- 新建一个名为
vue.config.js
的文件,文件内容为:
- 新建一个名为
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/Tic-Tac-Toe/dist/'
: '/'
}
- 将文件build,
npm run build
完成后会自动生成一个dist
文件夹(dist
文件夹用于存放编译后用于发布的线上代码),完成后我们将dist
文件夹上传到github上Tic-Tac-Toe
上即可
- 将文件build,
三:总结,遇到的坑
- 游戏结束时,我想使用
alert
函数来通知游戏结束,没想到先弹出来alert
,后才在页面上渲染最后一颗棋子
- 原因:div加载到页面需要时间,还没等加载到页面就执行到alert了,此时alert会阻塞线程,这里阻塞的是GUI渲染线程.所以等alert执行完毕后这个线程才会继续执行,将div内容显示到页面上
- 解决办法:给把
alert
放进setTimeout
中去执行
- 好,我想
alert
后页面重新渲染,添加this.reload()
函数。什么?函数未定义?
- 原因,
setTmeout
里的this
指向的是window
- 解决办法,先在外层赋值
let _this = this
,再在setTimeout
内部调用_this.reload()
- 啥,为什么我的
reload
函数明明执行里,页面却没有重新渲染?
- 原因,粗心忘记在组件上使用
v-if=Boolean
来控制页面了,加上 <Cell :n="n" @click="cellClick(0,$event)" v-if="update"/>
- 要点:
a)父子组件间相互传值
b)页面重新渲染: data重置,$nextTick()
函数的使用
c)各种条件的设置与判断
具体代码:见我的github=======https://github.com/PingerL/Tic-Tac-Toe
PS :项目的具体创建
- 在官网下载对应版本的Node.js,下载完后常规安装,打开终端,运行
node -v
来查看是否安装成功,若安装成功会显示对应的版本号
- 全局安装CLI,在终端里运行命令
sudo npm install -g @vue/cli
- 进入到要创建的文件夹的目录,比如我要创建的文件放在在桌面上名为89的文件夹下:
cd /Users/PingerL/Desktop/89
- 在当前文件夹下创建一个项目,项目名为demovue1,运行指令:
vue create demovue1
,选择默认方式创建,至此就创建好了一个项目的原型,具体创建可参考官网