Pull to refresh

Comments 22

Спасибо за статью! Рад что интерес к Svelte ростет.))

Несколько ремарок:

Svelte такого не умеет, или я просто не нашел, как это правильно сделать, поэтому пришлось использовать диспетчер событий

Svelte умеет ровно также:

<button class="square" on:click={() => handleClick(i)}>...</button>

Поэтому диспатчер тут лишний. Он нужен только для создания полностью кастомных событий компонентов. Более того, в вашем случае можно вообще автоматически «всплывать» событие клика на компоненте Square, а не вызывать коллбек. Делается это так:

<button class="square" on:click>...</button>


Далее можно ловить клик прямо на Square и вообще не передавать i в Square:

<Square value={square} on:click={e => handleClick(i)}/>


Заранее извиняюсь, но позволил себе переписать ваш пример немного более рационально.
Ваш вариант не рабочий.
«0» не поставить, победитель не определяется
Блин, в примере, который я правил не было нуля. Только крестники ставились.))) Короче не последняя версия видимо. Исправил пример на скорую руку. Можно конечно и еще проще написать. Спасибо что заметили!
const _squares = squares.slice();
_squares[i] = xIsNext ? 'X' : 'O';
squares = _squares;

Подскажите, а в чём суть этого финта с копией массива. Если написать это более простым способом явно поменяв по ключу напрямую — так не сработает?


Попробовал сам так:


squares[i] = xIsNext ? 'X' : 'O';

кажется всё работает.

Иммутабельность же. Помогает всем фреймворка и Svelte в том числе лучше «понимать» что изменилось в объекте. В Svelte есть специальная опция компилятора immutable: true, которую мы обычно используем в проектах. И всем советую. Собственно с этой опцией ваш способ работать не будет.

Понятно. И immer какой-нибудь наверное сюда уже не подключишь, Svelte ведь компилятор :( После стандартных redux-их {… } простыней видеть эти костыли в Svelte, конечно, неприятно.

C immer прекрасно работает. Не понимаю какое вообще отношение к этому имеет компиляция.

После стандартных redux-их {… } простыней видеть эти костыли в Svelte, конечно, неприятно.

Так используйте {… } никто также не мешает. Как вы обеспечиваете иммутабильностью Svelte вообще не волнует. Для реактивности важен только факт присвоения значения, а иммутабильность не обязательна и работает внутри. Флагом immutable: true вы лишь говорите Svelte — «тебе не стоит парится на объектами, если ссылка на них не изменилась».

И было бы интересно узнать что из примера выше вы считаете «костялем»? Вроде бы обычный js.
И было бы интересно узнать что из примера выше вы считаете «костялем»?

Очевидно же — две строки из 3-х. Натужная иммутабельность, которая плохо сказывается на читаемости, написании и поддержки кода. Пассаж про обычный js не понял, обычный js не запрещает писать костыли :)


Так используйте {… } никто также не мешает

Гхм… Вы меня наверное недопоняли. От них после redux уже тошно, а вы мне их ещё и в svelte тащить предлагаете? :) Я собственно потому и упомянул immer.


C immer прекрасно работает. Не понимаю какое вообще отношение к этому имеет компиляция.

Ну тут надо пробовать. Я пока близко в Svelte не присматривался. Насколько я понимаю, он выискивает все мутабельные операции и добавляет к ним явные setter-ы. Так что в случае immer тут будет всё зависеть от того, сможет ли он подхватить их, будет ли там callback, как на него отреагирует svelte. Нужно ли будет помещать такой блок в $: {} кодовый блок. Как я и написал выше — надо пробовать и смотреть что получится… Всё таки это компилятор и надо понимать что получится в итоговом js-коде, какие будут обёртки и стоит ли игра свеч.

Очевидно же — две строки из 3-х. Натужная иммутабельность, которая плохо сказывается на читаемости, написании и поддержки кода. Пассаж про обычный js не понял, обычный js не запрещает писать костыли :)

Это же мини-демо проектик. Тащить в него immer или immutablejs для «читаемой» иммутабильности, имхо, было бы лишним. Проще написать 2 дополнительных строчки на чистом js, понятные всем. Пассаж про обычный js был про это.

Гхм… Вы меня наверное недопоняли. От них после redux уже тошно, а вы мне их ещё и в svelte тащить предлагаете? :) Я собственно потому и упомянул immer.

Окей, я понял. Ни разу не являюсь фанатом redux, поэтому ничего не предлагаю. Говорю о том, что можете юзать в Svelte что хотите.

Ну тут надо пробовать. Я пока близко в Svelte не присматривался. Насколько я понимаю, он выискивает все мутабельные операции и добавляет к ним явные setter-ы. Так что в случае immer тут будет всё зависеть от того, сможет ли он подхватить их, будет ли там callback, как на него отреагирует svelte. Нужно ли будет помещать такой блок в $: {} кодовый блок. Как я и написал выше — надо пробовать и смотреть что получится…

Раз не пробовали, тогда поверьте тем, кто пробовали. Юзаем immer на одном проекте. Вот переписал пример с его использованием: REPL

Всё таки это компилятор и надо понимать что получится в итоговом js-коде, какие будут обёртки и стоит ли игра свеч.

Ничего особенного там в этом смысле не получается. Единственное правило — Svelte должен «увидеть» присвоение в стейт. Можно считать что = это автоматический вызов setState. Если подробнее:

// не будет перерисовки, даже если immutable=false
arr.push(item);

// будет перерисовка, если immutable=false
arr.push(item);
arr = arr; // триггер дла set state

// НЕ будет перерисовка, если immutable=true
arr.push(item);
arr = arr;

// будет перерисовка, если immutable=true
arr = [ ...arr, item ];

Последний вариант самый правильный и стоит юзать immutable=true. Как именно вы будете делать иммутабильность для Svelte не важно. В случае с immer:

arr = produce(arr, _arr => _arr.push(item));

// скомпилируется всего лишь в это:

$$invalidate('arr', arr = produce(arr, _arr => _arr.push(item)));
Если мы собираемся записывать каждое состояние игры в историю, то необходимо полное копирование массива. Если история не нужна, то можно и не создавать копию.
Спасибо за подсказку! В ближайшие дни я исправлю примеры и обновлю статью.
А зачем в beforeUpdate каждый раз проверять статус? Если можно при изменении перерасчет сделать.

Svelte: reactive declarations

$: winner = calculateWinner(state.squares);
$: status = winner ? `Winner: ${winner}` : `Next player: ${(state.xIsNext ? 'X' : 'O')}`;
//или
$: status = (() => {
  const winner = calculateWinner(state.squares);
  return winner ? `Winner: ${winner}` : `Next player: ${(state.xIsNext ? 'X' : 'O')}`;
})();


И было бы круто примеры кода сразу в статье видеть. Утомляет каждый раз по ссылке переходить.
$: status = (() => {
const winner = calculateWinner(state.squares);
return winner? `Winner: ${winner}`: `Next player: ${(state.xIsNext? 'X': 'O')}`;
})();

Воу, зачем так сложно)) Это же не JSX какой-то, а обычны JS (по крайней мере синтаксически ;-) ). Можно делать блочные реактивные декларации:

let status;
$: {
  const winner = calculateWinner(squares);
  status = winner ? `Winner: ${winner}` : `Next player: ${xIsNext ? 'X' : 'O'}`;
}

Но в целом, я бы не стал увлекаться конкатенацией строк в скриптах и полностью перевел бы это дело в шаблон. На случай если вывод статуса нужно будет как-то дополнительно задизайнить, например `Winner: ${winner}` выводить жирным:

<div class="status">
{#if winner}
  <b>Winner: {winner}</b>
{:else}
  Next player: {xIsNext ? 'X' : 'O'}
{/if}
</div>

Все таки в Svelte у нас html-first и прекрасный DSL для этого.
Понятно, а если еще добавить статус ничьи?
это еще один статус\состояние игры. Победитель, ничья, следующий ход.

я бы так делал:
{#if state === 'победа' }
  ...
{:else if state === 'ничья'}
  ...
{:else if state === 'ход'}
  ...
{/if}
Ну да, типа того. Возможно если статусов станет больше и одного лишь наличия winner будет недостаточно, чтобы понять какую часть шаблона нужно отрисовать, то придетяся считаться какой-то status в скрипте. Вообще сильно зависит от логики определения. Например, возможно будет достаточно такой конструкции:

<script>
  ...
  $: winner = calculateWinner(squares);
  $: draw = ! squares.includes('') && ! winner;
  ...
</script>

<div class="status">
{#if winner}
  Winner: {winner}
{:else if draw}
  Draw!
{:else}
  Next player: {xIsNext ? 'X' : 'O'}
{/if}
</div>


Обновил мой пример. Тот же draw пригождается еще и для отмены click()
Да, beforeUpdate можно было бы и не вводить. Кстати, в туториале для React'a статус тоже почему-то определяется в методе render.

Я, думаю, сделаю новую редакцию статьи и там размещу код. Здесь как, принято ли новую редакцию статьи в виде отдельной статьи оформлять? Если исправить прямо здесь, то новым читателям уже не будут понятны старые комментарии.
Лучше исправить текущую. Делать еще одну статью с правками, но тем же смыслом не стоит.
Начал исправлять статью и исходный код в соответствии с замечаниями. Пока еще не до конца.
Sign up to leave a comment.

Articles