Как стать автором
Обновить

Tic Tac Toe, часть 0: Сравнение Svelte и React

Время на прочтение4 мин
Количество просмотров9.6K
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

ReactSvelte


App.svelte
<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>

Board.svelte
<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>

Square.svelte
<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

ReactSvelte
В 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

ReactSvelte
По клику в клетке появляется крестик. В Square добавлен обработчик события DOM handleClick для клика мышкой. Добавлена переменная state для отображения крестика в клетке.


<script>
    export let value = '';
    let state = '';

    function handleClick() {
        state = 'X';
    }
</script>

<button on:click={handleClick}>
    {state}
</button>

Lifting State Up

ReactSvelte
До этого момента состояние клеток хранилось в них самих. Сейчас хранение состояния игры перенесено в компонент Board, состояние всех клеток хранится в одном массиве. Обработчик клика handleClick также перенесен в компонент Board. Square теперь снова отображает состояние клетки с помощью свойства value.


Board.svelte
<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>

Square.svelte
<script>
    export let value = '';
</script>

<button on:click>
    {value}
</button>

Taking Turns

ReactSvelte
Добавлено появление нолика после крестика.


Board.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

ReactSvelte
Добавлена функция определения победителя calculateWinner в отдельном файле helper.js. Запрещен клик по уже установленным клеткам и после победы.


heplers.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;
}  

Board.svelte
<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.

Теги:
Хабы:
Всего голосов 27: ↑21 и ↓6+15
Комментарии22

Публикации

Истории

Работа

Ближайшие события