今天的教程是一个综合案例,在这个案例中你将学会如何创建组件,如何监听事件,如何在组件间传值,如何定义计算属性,如何循环渲染,如何条件渲染等
井字棋一共九个格子,我们先定义一个组件Square.vue生成格子
- <div class="square">
- {{ value }}
- div>
-
- <script setup>
- // 使用 <script setup>
- defineProps({
- value: [String, Number]
- })
- script>
-
- <style scoped>
- .square {
- background: #fff;
- border: 1px solid #999;
- float: left;
- font-size: 24px;
- font-weight: bold;
- line-height: 34px;
- height: 34px;
- margin-right: -1px;
- margin-top: -1px;
- padding: 0;
- text-align: center;
- width: 34px;
- }
- style>
再定义一个棋盘组件Board用来生成棋盘
- <div>
- <template v-for="x in 3">
- <div class="board-row">
- <template v-for="y in 3">
- <Square :value="(x-1) * 3 + y">Square>
- template>
- div>
- template>
- div>
-
- <script setup>
- import Square from "@/views/basic/Square.vue";script>
-
- <style scoped>
- .board-row:after {
- clear: both;
- content: '';
- display: table;
- }
- style>
效果如下

我们定义一个squares数组,初始值都是null,每次点击的时候把对应值置为X,就能实现点击后格子显示X
<Square :value="squares[(x-1) * 3 + y - 1]" @click="clickSquare((x-1) * 3 + y - 1)">Square>
- import { reactive } from 'vue'
- // 初始化棋盘数据
- const squares = reactive(new Array(9).fill(null))
-
- // 点击格子事件
- function clickSquare(index) {
- squares[index] = 'X'
- }

第一个点击是X,第二个点击是圈,需要加下判断
- let clickCount = 0
-
- // 点击格子事件
- function clickSquare(index) {
- // 格子中有值不能被覆盖
- if (squares[index]) return
- // 轮流落子
- if (clickCount % 2 == 0) {
- squares[index] = 'X'
- } else {
- squares[index] = 'O'
- }
- clickCount++
- }
通过calculateWinner函数来判断优胜者,完整代码如下
- <div class="container">
- <div>
- <template v-for="x in 3">
- <div class="board-row">
- <template v-for="y in 3">
- <Square :value="squares[(x-1) * 3 + y - 1]" @click="clickSquare((x-1) * 3 + y - 1)">Square>
- template>
- div>
- template>
- div>
- <div style="margin-left: 10px">
- <div v-if="winner == null">下个旗手: {{nextPlayer}}div>
- <div v-else>获胜者: {{winner}}div>
- div>
- div>
-
- <script setup>
- import Square from "@/views/basic/Square.vue";
- import {computed, reactive, ref} from 'vue'
- // 初始化棋盘数据
- const squares = reactive(new Array(9).fill(null))
- let clickCount = ref(0)
- let winner = ref(null)
-
- // 点击格子事件
- function clickSquare(index) {
- // 格子中有值或者存在优胜者则返回
- if (squares[index] || winner.value) return
- // 轮流落子
- if (clickCount.value % 2 == 0) {
- squares[index] = 'X'
- } else {
- squares[index] = 'O'
- }
- winner.value = calculateWinner(squares)
- clickCount.value++
- }
-
- // 计算属性,通过clickCount计算出下个旗手
- const nextPlayer = computed(() => {
- return clickCount.value % 2 == 0 ? 'X' : 'O'
- })
-
- function calculateWinner(squares) {
- const lines = [
- [0, 1, 2],
- [3, 4, 5],
- [6, 7, 8],
- [0, 3, 6],
- [1, 4, 7],
- [2, 5, 8],
- [0, 4, 8],
- [2, 4, 6]
- ];
- for (let i = 0; i < lines.length; i++) {
- const [a, b, c] = lines[i];
- if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
- return squares[a];
- }
- }
- return null;
- }
- script>
-
- <style scoped>
- .container {
- display: flex;
- }
-
- .board-row:after {
- clear: both;
- content: '';
- display: table;
- }
- style>