做一个井字棋
思路
- 真的不会,怎么办
- 三行不会,先做一行
- 一行不会,先做一个,一个框可
X
可O
- 也不会,那先做一个框点一下出现
X
动手做
- 第一步,实现刷新页面等概率出现
X
或O
<template>
<div class="new">
<div v-if="Math.random() > 0.5">X</div>
<div v-else>O</div>
</div>
</template>
<script></script>
<style></style>
这里我们学习到条件语句
<template>
<div class="new">
<div v-if="true">X</div>
<div v-else>O</div> //结果页面显示 X
<div v-if="false">X</div>
<div v-else>O</div> //结果页面显示 O
</div>
</template>
- data 的使用
<template>
<div class="new">
<div v-if="a">X</div>
<div v-else>空</div>
</div>
</template>
<script>
export default {
data() { //ES6 语法,表示一个函数
return { a: false }
}
}
</script>
页面默认没有显示内容
- 接下来监听事件
<template>
<div class="new">
<div v-if="a">X</div>
<div v-else v-on:click="a = true">空</div>
</div>
</template>
页面为空,点击后为 X
- 优化下代码
打开控制台,检查元素,发现X
被<div>X</div>
包裹,如何去掉多余的<div></div>
<template>
<div class="new">
<template v-if="a">X</template>
<template v-else v-on:click="a = true">空</template>
</div>
</template>
出现 bug 了,元素被隐藏,绑定的事件就无效了,所以应该这样绑定事件
<template>
<div class="new" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else>空</template>
</div>
</template>
- 实现一个空白格子点击后出现 X
<template>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
</template>
<script>
export default {
data() { //ES6 语法,表示一个函数
return { a: false }
}
}
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
- 一个格子变成一行格子
<template>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
</template>
报错,原因:vue
的template
里面只能有一个根元素,就是只能有一个div
,所以要用一个div
把它们包起来
<template>
<div>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
</div>
</template>
又出现 bug 了,点击一个,三个都变成X
了,因为点击后都是"a = true"
,解决方法,给每个编号(傻瓜办法),这时候我们想到用组件
- 如何创建一个组件
App.vue 同目录下,创建 Cell.vue
Cell.vue
<template>
<div class="cell" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else></template>
</div>
</template>
<script>
export default {
data() {
return { a: false }
}
}
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
App.vue
<template>
<div>
<Cell />
<Cell />
<Cell />
</div>
</template>
<script>
import Cell from "./Cell.vue" // .vue 后缀可以省略
export default {
components: { Cell }
}
</script>
<style></style>
- 实现九个格子
实现空白棋盘,点击后出现X
App.vue
<template>
<div>
<div class="row">
<Cell />
<Cell />
<Cell />
</div>
<div class="row">
<Cell />
<Cell />
<Cell />
</div>
<div class="row">
<Cell />
<Cell />
<Cell />
</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell }
};
</script>
<style>
.row {
display: flex;
}
</style>
- 点击后的内容随
text: "值"
改变而改变
Cell.vue
<template>
<div class="cell" v-on:click="a = true">
<template v-if="a">{{text}}</template> //---
<template v-else></template>
</div>
</template>
<script>
export default {
data() {
return { a: false, text: "O" } //---
}
}
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
但是怎么实现用户的点击次数不同而改变的,这是这个项目的最复杂的地方
- 侦听事件
App.vue
<template>
<div>
<div class="row">
<Cell v-on:click="onClickCell" /> //---
<Cell v-on:click="onClickCell" />
<Cell v-on:click="onClickCell" />
</div>
<div class="row">
<Cell v-on:click="onClickCell" />
<Cell v-on:click="onClickCell" />
<Cell v-on:click="onClickCell" />
</div>
<div class="row">
<Cell v-on:click="onClickCell" />
<Cell v-on:click="onClickCell" />
<Cell v-on:click="onClickCell" />
</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
methods: { //---
onClickCell() {
console.log('某个 cell 被点击了')
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
直接这样写是没有用的,需要在 Cell.vue 里面手动的通知一下
Cell.vue
<template>
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template> //---
<template v-else></template>
</div>
</template>
<script>
export default {
data() {
return { a: false, text: "O" }
},
methods: { //---
onClickSelf() {
this.a = true;
this.$emit('click');
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
监听事件:在外面写v-on:click=""
,在里面写this.$emit('click')
,这是相对应的
- 当前被点的是第几次
- 写个
div
显示在头部,表示这是第几次被点击 - 把
n
通知给所有 Cell
App.vue
<template>
<div>
<div>n 的值为 {{n}}</div> //---
<div class="row">
<Cell v-on:click="onClickCell" v-bind:n="n" /> //---
<Cell v-on:click="onClickCell" v-bind:n="n" />
<Cell v-on:click="onClickCell" v-bind:n="n" />
</div>
<div class="row">
<Cell v-on:click="onClickCell" v-bind:n="n" />
<Cell v-on:click="onClickCell" v-bind:n="n" />
<Cell v-on:click="onClickCell" v-bind:n="n" />
</div>
<div class="row">
<Cell v-on:click="onClickCell" v-bind:n="n" />
<Cell v-on:click="onClickCell" v-bind:n="n" />
<Cell v-on:click="onClickCell" v-bind:n="n" />
</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
data() {
return { n: 0 } //---
}
methods: {
onClickCell() {
console.log('某个 cell 被点击了')
this.n = this.n + 1; //---
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
如何让组件接收到外面的n
Cell.vue
<template>
<div>
{{n}} //---
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</div>
</template>
<script>
export default {
props: ["n"], //---
data() {
return { a: false, text: "O" }
},
methods: {
onClickSelf() {
this.a = true;
this.$emit('click');
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
接收数据:在外面写v-bind:n="n"
,在里面写props: ["n"]
,这是相对应的
- 一些简写
v-on:click="onClickCell"
==> @click="onClickCell"
v-bind:n="n"
==> :n="n"
- 通过计算得点击后的结果
Cell.vue
<template>
<div>
{{n}} //---
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</div>
</template>
<script>
export default {
props: ["n"], //---
data() {
return { a: false } //---
},
conputed: { //计算属性
text() {
return this.n % 2 == 0 ? "X" : "O";
}
}
methods: {
onClickSelf() {
this.a = true;
this.$emit('click');
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
出现 bug 第一次点击为O
,第二次点击为X
,但是连同之前的都变为X
,第三次点击都变为O
...
原因:每次n
变化text(){}
也跟着重新变化
所以不能用计算属性,返回之前的做法,在点击的时候确定
- 返回之前的做法
Cell.vue
<template>
<div>
{{n}}
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</div>
</template>
<script>
export default {
props: ["n"],
data() {
return { a: false, text: "" } ; //---
},
methods: {
onClickSelf() {
this.a = true;
this.text = this.n % 2 === 0 ? "X" : "O"; //---
this.$emit('click');
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
又出现 bug 了,可以重复点击
- 解决重复点击发生改变的问题
Cell.vue
<template>
<div>
{{n}}
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</div>
</template>
<script>
export default {
props: ["n"],
data() {
return { a: false, text: "" } ;
},
methods: {
onClickSelf() {
if (this.text === "") { //---
return;
}
this.a = true;
this.text = this.n % 2 === 0 ? "X" : "O";
this.$emit('click');
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
加个判断语句就可以解决了
- 给点击事件编号
App.vue
<template>
<div>
<div>n 的值为 {{n}}</div> //---
<div class="row">
<Cell @click="onClickCell(0)" :n="n" /> //---
<Cell @click="onClickCell(1)" :n="n" />
<Cell @click="onClickCell(2)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(3)" :n="n" />
<Cell @click="onClickCell(4)" :n="n" />
<Cell @click="onClickCell(5)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(6)" :n="n" />
<Cell @click="onClickCell(7)" :n="n" />
<Cell @click="onClickCell(8)" :n="n" />
</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
data() {
return { n: 0 }
}
methods: {
onClickCell(i) { //---
console.log(`${i}号被点击`) //---
this.n = this.n + 1;
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
正确的写法应该是写个函数<Cell @click="() => onClickCell(0)" :n="n" />
,但是vue
做了个语法糖,如果发现你直接调用一个函数,它会等click
的时候再去调用,不会直接调用
点击后console.log
几号被点击,所以我们知道哪个被点击了
但是我们不知道点击的是X
还是O
- 怎么知道点击的是
X
还是O
Cell.vue
<template>
<div>
{{n}}
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</div>
</template>
<script>
export default {
props: ["n"],
data() {
return { a: false, text: "" } ;
},
methods: {
onClickSelf() {
if (this.text === "") {
return;
}
this.a = true;
this.text = this.n % 2 === 0 ? "X" : "O";
this.$emit('click', this.text); //给点击事件加一个参数
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
但是我怎么拿到这个内容呢
App.vue
<template>
<div>
<div>n 的值为 {{n}}</div>
<div class="row">
<Cell @click="onClickCell(0, $event)" :n="n" /> //---
<Cell @click="onClickCell(1, $event)" :n="n" />
<Cell @click="onClickCell(2, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(3, $event)" :n="n" />
<Cell @click="onClickCell(4, $event)" :n="n" />
<Cell @click="onClickCell(5, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(6, $event)" :n="n" />
<Cell @click="onClickCell(7, $event)" :n="n" />
<Cell @click="onClickCell(8, $event)" :n="n" />
</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
data() {
return { n: 0 }
}
methods: {
onClickCell(i, text) { //---
console.log(`${i}号被点击,内容是:${text}`) //---
this.n = this.n + 1;
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
$event
的写法就是接收里面$emit
的数据
- 创建一个二维数组保存被点击的结果
App.vue
<template>
<div>
<div>n 的值为 {{n}}</div>
<div class="row">
<Cell @click="onClickCell(0, $event)" :n="n" /> //---
<Cell @click="onClickCell(1, $event)" :n="n" />
<Cell @click="onClickCell(2, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(3, $event)" :n="n" />
<Cell @click="onClickCell(4, $event)" :n="n" />
<Cell @click="onClickCell(5, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(6, $event)" :n="n" />
<Cell @click="onClickCell(7, $event)" :n="n" />
<Cell @click="onClickCell(8, $event)" :n="n" />
</div>
<div>{{map}}</div> //打出map ---
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
data() {
return {
n: 0,
map: [ //---
[null, null, null],
[null, null, null],
[null, null, null]
],
}
}
methods: {
onClickCell(i, text) {
console.log(`${i}号被点击,内容是:${text}`)
this.map[Math.floor(i / 3)][i % 3] = text; //结果传入数组 ---
this.n = this.n + 1;
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
- 判断输赢
App.vue
<template>
<div>
<div>n 的值为 {{n}}</div>
<div class="row">
<Cell @click="onClickCell(0, $event)" :n="n" /> //---
<Cell @click="onClickCell(1, $event)" :n="n" />
<Cell @click="onClickCell(2, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(3, $event)" :n="n" />
<Cell @click="onClickCell(4, $event)" :n="n" />
<Cell @click="onClickCell(5, $event)" :n="n" />
</div>
<div class="row">
<Cell @click="onClickCell(6, $event)" :n="n" />
<Cell @click="onClickCell(7, $event)" :n="n" />
<Cell @click="onClickCell(8, $event)" :n="n" />
</div>
<div>{{map}}</div>
</div>
</template>
<script>
import Cell from "./Cell.vue" ;
export default {
components: { Cell },
data() {
return {
n: 0,
map: [
[null, null, null],
[null, null, null],
[null, null, null]
],
}
}
methods: {
onClickCell(i, text) {
console.log(`${i}号被点击,内容是:${text}`)
this.map[Math.floor(i / 3)][i % 3] = text;
this.n = this.n + 1;
this.tell(); //---
},
tell() { //---
for (let i = 0; i < 3; i++) {
if (
this.map[i][0] !== null &&
this.map[i][0] == this.map[i][1] &&
this.map[i][1] == this.map[i][2]
) { //行判断
this.result = this.map[i][0];
}
}
for (let j = 0; j < 3; j++) {
if (
this.map[0][j] !== null &&
this.map[0][j] == this.map[1][j] &&
this.map[1][j] == this.map[2][j]
) { //列判断
this.result = this.map[0][j];
}
}
if (
this.map[0][0] !== null &&
this.map[0][0] == this.map[1][1] &&
this.map[1][1] == this.map[2][2]
) { //斜判断
this.result = this.map[0][0];
}
if (
this.map[0][2] !== null &&
this.map[0][2] == this.map[1][1] &&
this.map[1][1] == this.map[2][0]
) { //斜判断
this.result = this.map[0][2];
}
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
- 样式优化
App.vue
<template>
<div class="wrapper">
<div>第{{n}}手</div>
<div class="chess">
<div class="row">
<Cell v-on:click="onClickCell(0, $event)" :n="n" />
<Cell v-on:click="onClickCell(1, $event)" :n="n" />
<Cell v-on:click="onClickCell(2, $event)" :n="n" />
</div>
<div class="row">
<Cell v-on:click="onClickCell(3, $event)" :n="n" />
<Cell v-on:click="onClickCell(4, $event)" :n="n" />
<Cell v-on:click="onClickCell(5, $event)" :n="n" />
</div>
<div class="row">
<Cell v-on:click="onClickCell(6, $event)" :n="n" />
<Cell v-on:click="onClickCell(7, $event)" :n="n" />
<Cell v-on:click="onClickCell(8, $event)" :n="n" />
</div>
</div>
<div>结果:{{result == null ? `胜负未分` : `胜方为${result}`}}</div>
</div>
</template>
<script>
import Cell from "./Cell";
export default {
components: { Cell },
data() {
return {
n: 0,
map: [[null, null, null], [null, null, null], [null, null, null]],
result: null
};
},
methods: {
onClickCell(i, text) {
console.log(`${i}号被点击,内容是:${text}`);
this.map[Math.floor(i / 3)][i % 3] = text;
this.n = this.n + 1;
this.tell();
},
tell() {
for (let i = 0; i < 3; i++) {
if (
this.map[i][0] !== null &&
this.map[i][0] == this.map[i][1] &&
this.map[i][1] == this.map[i][2]
) {
this.result = this.map[i][0];
}
}
for (let j = 0; j < 3; j++) {
if (
this.map[0][j] !== null &&
this.map[0][j] == this.map[1][j] &&
this.map[1][j] == this.map[2][j]
) {
this.result = this.map[0][j];
}
}
if (
this.map[0][0] !== null &&
this.map[0][0] == this.map[1][1] &&
this.map[1][1] == this.map[2][2]
) {
this.result = this.map[0][0];
}
if (
this.map[0][2] !== null &&
this.map[0][2] == this.map[1][1] &&
this.map[1][1] == this.map[2][0]
) {
this.result = this.map[0][2];
}
}
}
};
</script>
<style>
.row {
display: flex;
}
.wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
Cell.vue
<template>
<div class="cell" v-on:click="onClickSelf">
<template v-if="a">{{text}}</template>
<template v-else></template>
</div>
</template>
<script>
export default {
props: ["n"],
data() {
// ES6 语法
return { a: false, text: ""}
},
methods: {
onClickSelf() {
if (this.text !== "") {
return;
}
this.a = true;
this.text = this.n % 2 == 0 ? "x" : "o";
this.$emit('click', this.text);
}
}
}
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
- 部署到 GitHub 上
- 控制台命令行
yarn build
- 上传到 GitHub 上,预览
+/vue-demo-1/index.html
报错
打开控制台,查看Request URL
,发现缺少一个目录
访问+/vue-demo-1/dist/+Ruquest URL地址
- 配置路径
参考资料
复制
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/my-project/'
: '/'
}
修改
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/vue-demo-1/dist/' //---
: '/'
}
- 重新
yarn build
,上传,访问https://liujianli000.github.io/vue-demo-1/dist/index.html