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.
