做一個井字棋
思路
- 真的不會,怎么辦
- 三行不會,先做一行
- 一行不會,先做一個,一個框可
X可O - 也不會,那先做一個框點(diǎn)一下出現(xiàn)
X
動手做
- 第一步,實(shí)現(xiàn)刷新頁面等概率出現(xiàn)
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>
這里我們學(xué)習(xí)到條件語句
<template>
<div class="new">
<div v-if="true">X</div>
<div v-else>O</div> //結(jié)果頁面顯示 X
<div v-if="false">X</div>
<div v-else>O</div> //結(jié)果頁面顯示 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 語法,表示一個函數(shù)
return { a: false }
}
}
</script>
頁面默認(rèn)沒有顯示內(nèi)容
- 接下來監(jiān)聽事件
<template>
<div class="new">
<div v-if="a">X</div>
<div v-else v-on:click="a = true">空</div>
</div>
</template>
頁面為空,點(diǎn)擊后為 X
- 優(yōu)化下代碼
打開控制臺,檢查元素,發(fā)現(xiàn)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>
出現(xiàn) bug 了,元素被隱藏,綁定的事件就無效了,所以應(yīng)該這樣綁定事件
<template>
<div class="new" v-on:click="a = true">
<template v-if="a">X</template>
<template v-else>空</template>
</div>
</template>
- 實(shí)現(xiàn)一個空白格子點(diǎn)擊后出現(xiàn) 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 語法,表示一個函數(shù)
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>
又出現(xiàn) bug 了,點(diǎn)擊一個,三個都變成X了,因?yàn)辄c(diǎn)擊后都是"a = true",解決方法,給每個編號(傻瓜辦法),這時候我們想到用組件
- 如何創(chuàng)建一個組件
App.vue 同目錄下,創(chuàng)建 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>
- 實(shí)現(xiàn)九個格子
實(shí)現(xiàn)空白棋盤,點(diǎn)擊后出現(xiàn)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>
- 點(diǎn)擊后的內(nèi)容隨
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>
但是怎么實(shí)現(xiàn)用戶的點(diǎn)擊次數(shù)不同而改變的,這是這個項(xiàng)目的最復(fù)雜的地方
- 偵聽事件
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 被點(diǎn)擊了')
}
}
};
</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>
監(jiān)聽事件:在外面寫v-on:click="",在里面寫this.$emit('click'),這是相對應(yīng)的
- 當(dāng)前被點(diǎn)的是第幾次
- 寫個
div顯示在頭部,表示這是第幾次被點(diǎn)擊 - 把
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 被點(diǎn)擊了')
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>
接收數(shù)據(jù):在外面寫v-bind:n="n",在里面寫props: ["n"],這是相對應(yīng)的
- 一些簡寫
v-on:click="onClickCell" ==> @click="onClickCell"
v-bind:n="n" ==> :n="n"
- 通過計(jì)算得點(diǎn)擊后的結(jié)果
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: { //計(jì)算屬性
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>
出現(xiàn) bug 第一次點(diǎn)擊為O,第二次點(diǎn)擊為X,但是連同之前的都變?yōu)?code>X,第三次點(diǎn)擊都變?yōu)?code>O...
原因:每次n變化text(){}也跟著重新變化
所以不能用計(jì)算屬性,返回之前的做法,在點(diǎ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: "" } ; //---
},
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>
又出現(xiàn) bug 了,可以重復(fù)點(diǎn)擊
- 解決重復(fù)點(diǎn)擊發(fā)生改變的問題
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>
加個判斷語句就可以解決了
- 給點(diǎn)擊事件編號
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}號被點(diǎn)擊`) //---
this.n = this.n + 1;
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
正確的寫法應(yīng)該是寫個函數(shù)<Cell @click="() => onClickCell(0)" :n="n" />,但是vue做了個語法糖,如果發(fā)現(xiàn)你直接調(diào)用一個函數(shù),它會等click的時候再去調(diào)用,不會直接調(diào)用
點(diǎn)擊后console.log幾號被點(diǎn)擊,所以我們知道哪個被點(diǎn)擊了
但是我們不知道點(diǎn)擊的是X還是O
- 怎么知道點(diǎn)擊的是
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); //給點(diǎn)擊事件加一個參數(shù)
}
}
};
</script>
<style>
.cell {
border: 1px solid black;
width: 100px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 80px;
}
</style>
但是我怎么拿到這個內(nèi)容呢
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}號被點(diǎn)擊,內(nèi)容是:${text}`) //---
this.n = this.n + 1;
}
}
};
</script>
<style>
.row {
display: flex;
}
</style>
$event的寫法就是接收里面$emit的數(shù)據(jù)
- 創(chuàng)建一個二維數(shù)組保存被點(diǎn)擊的結(jié)果
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}號被點(diǎn)擊,內(nèi)容是:${text}`)
this.map[Math.floor(i / 3)][i % 3] = text; //結(jié)果傳入數(shù)組 ---
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}號被點(diǎn)擊,內(nè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>
- 樣式優(yōu)化
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>結(jié)果:{{result == null ? `勝負(fù)未分` : `勝方為${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}號被點(diǎn)擊,內(nè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 上,預(yù)覽
+/vue-demo-1/index.html報錯
打開控制臺,查看Request URL,發(fā)現(xiàn)缺少一個目錄
訪問+/vue-demo-1/dist/+Ruquest URL地址 - 配置路徑
參考資料
復(fù)制
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