Tic Tac Toe, часть 0: Сравнение Svelte и React
Tic Tac Toe, часть 1: Svelte и Canvas 2D
Tic Tac Toe, часть 2: Undo/Redo с хранением состояний
Tic Tac Toe, часть 3: Undo/Redo с хранением команд
Tic Tac Toe, часть 4: Взаимодействие с бэкендом на Flask с помощью HTTP
На сайте React'a есть туториал, в котором описывается разработка игры Tic Tac Toe. Я решил повторить разработку этой игры на Svelte. Статья охватывает только первую половину туториала, до реализации истории ходов. Для целей ознакомления с фреймворком этого вполне достаточно. Каждый раздел статьи соответствует разделу туториала, содержит ссылки на исходный код обоих фреймворков.
Inspecting the Starter Code
<script>
import Board from './Board.svelte';
</script>
<div class="game">
<div class="game-board">
<Board />
</div>
<div class="game-info">
<div></div>
<ol></ol>
</div>
</div>
<style>
.game {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
ol {
padding-left: 30px;
}
</style>
<script>
import Square from './Square.svelte';
</script>
<div class="status">Next player: X</div>
<div class="board">
{#each Array(9) as square}
<Square />
{/each}
</div>
<style>
.board {
width: 102px;
}
.status {
margin-bottom: 10px;
}
</style>
<button></button>
<style>
button {
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;
margin-bottom: -1px;
padding: 0;
text-align: center;
width: 34px;
}
button:focus {
outline: none;
}
</style>
Каждый компонент выполняется в отдельном файле. Компонент может содержать в себе код, html разметку и css стили. Показано использование вложенных компонентов: компонент Square импортируется в компонент Board, компонент Board импортируется в компонент App. Показано использование блока each в компоненте Board. Стили меняются редко, поэтому разместил их после html разметки, чтобы лишний раз не пролистывать их.
Passing Data Through Props
React — Svelte
В Square объявлено свойство value.
<script>
export let value = '';
</script>
<button>{value}</button>
В Board показано использование индексов массива для заполнения клеток.
<div class="board">
{#each Array(9) as square, i}
<Square value={i}/>
{/each}
</div>
Making an Interactive Component
React — Svelte
По клику в клетке появляется крестик. В Square добавлен обработчик события DOM handleClick для клика мышкой. Добавлена переменная state для отображения крестика в клетке.
<script>
export let value = '';
let state = '';
function handleClick() {
state = 'X';
}
</script>
<button on:click={handleClick}>
{state}
</button>
Lifting State Up
React — Svelte
До этого момента состояние клеток хранилось в них самих. Сейчас хранение состояния игры перенесено в компонент Board, состояние всех клеток хранится в одном массиве. Обработчик клика handleClick также перенесен в компонент Board. Square теперь снова отображает состояние клетки с помощью свойства value.
<script>
import Square from './Square.svelte';
let state = {
squares: Array(9).fill(''),
};
function handleClick(i) {
const squares = state.squares.slice();
squares[i] = 'X';
state.squares = squares;
}
</script>
<div class="status">Next player: X</div>
<div class="board">
{#each state.squares as value, i}
<Square {value} on:click={e => handleClick(i)}/>
{/each}
</div>
<script>
export let value = '';
</script>
<button on:click>
{value}
</button>
Taking Turns
React — Svelte
Добавлено появление нолика после крестика.
<script>
import Square from './Square.svelte';
let state = {
squares: Array(9).fill(''),
xIsNext: true,
};
function handleClick(i) {
const squares = state.squares.slice();
squares[i] = state.xIsNext ? 'X' : 'O';
state.squares = squares;
state.xIsNext = !state.xIsNext;
}
</script>
<div class="status">Next player: {state.xIsNext ? 'X' : 'O'}</div>
<div class="board">
{#each state.squares as value, i}
<Square {value} on:click={e => handleClick(i)}/>
{/each}
</div>
Declaring a Winner
React — Svelte
Добавлена функция определения победителя calculateWinner в отдельном файле helper.js. Запрещен клик по уже установленным клеткам и после победы.
export 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>
import Square from './Square.svelte';
import { calculateWinner } from './helpers.js';
let state = {
squares: Array(9).fill(''),
xIsNext: true,
};
$: winner = calculateWinner(state.squares);
function handleClick(i) {
if (winner || state.squares[i])
return;
const squares = state.squares.slice();
squares[i] = state.xIsNext ? 'X' : 'O';
state.squares = squares;
state.xIsNext = !state.xIsNext;
}
</script>
<div class="status">
{#if winner}
<b>Winner: {winner}</b>
{:else}
Next player: {state.xIsNext ? 'X' : 'O'}
{/if}
</div>
<div class="board">
{#each state.squares as value, i}
<Square {value} on:click={e => handleClick(i)} />
{/each}
</div>
Дальше проходить туториалу не планирую, с фреймворком ознакомился.
Репозиторий на GitHub
https://github.com/nomhoi/tic-tac-toe
Установка игры на локальном компьютере:
git clone git@github.com:nomhoi/tic-tac-toe.git
cd tic-tac-toe
npm install
npm run dev
Запускаем игру в браузере по адресу: http://localhost:5000/.
UPDATE: Исправлена статья и исходный код в соответствии с замечаниями в комментариях.
UPDATE2: Добавлен репозиторий туториала на GitHub.