Буквально вчера вышла 2-я версия молодого, но весьма многообещающего фреймворка SvelteJS. Версия мажорная, а значит содержит не только новые фичи и исправленные баги, но и соответствующие «breaking changes». Что новенького предлагает разработчикам новая версия и почему Svelte стал еще лучше, читайте под катом.

Если вдруг, по какой-то неведомой причине, вы не знаете что такое Svelte и почему это не «yet another javascript framework». Предлагаю сперва наверстать упущенное, чтобы лучше понимать о чем речь.
Самое очевидное и глобальное изменение в новой версии — кардинальная смена синтаксиса шаблонов. Рич наконец-то решил избавиться от «усо»-подобного синтаксиса в пользу более лаконичного варианта:
Было
Стало
Очевидно что синтаксис стал визуально проще и чище. Изменения коснулись всех конструкций в шаблонах, в том числе специальных элементов Svelte:
Было
Стало
Предыдущий синтаксис специальных элементов был уж слишком необычен и большинство редакторов кода не справлялись с его подсветкой. Новый синтаксис напоминает синтаксис namespace из XML и значительно лучше воспринимается редакторами.
Надо отдать должное Ричу и отдельно отметить, что все изменения активно обсуждались с сообществом, а некоторые мои предложения вошли в финальный вариант синтаксиса и, вроде как, даже способствовали упрощению парсера.
В общем изменений в синтаксисе много и это могло бы стать проблемой для миграции на новую версию, если бы не утилита svelte-upgrade, специально созданная для автоматического апгрейда Svelte-компонентов. Полный список изменений, можно посмотреть там же.
Годно, Рич! Прощайте «усы»!

Так как Svelte — это прежде всего компилятор, стоит сначала отметить, что итоговый код предыдущей версии компилировался в ES5. Поэтому для поддержки IE11 и других «прогрессивных» версий браузеров, не было нужды связываться с транспиллерами вроде Babel или Bublé.
Но на дворе 2018-й и чтобы компилятор мог продуцировать более оптимальный и еще более компактный код, было принято решение отказаться от поддержки ES5. Иными словами, теперь Svelte компилирует компоненты в ES6 и нам придется использовать транспиллер, если необходима поддержка старых версий.
Сам я полностью поддерживаю такой подход. Тем более что подключить Babel к Webpack или Rollup, уверен, ни для кого уже не составит труда. Особенно если учесть, что использовать Svelte без оных все равно не получится. ;-)
До сих пор не понимаю почему эта фича называется actions, но для себя решил, что носителям языка виднее. Хотя лично для меня — это не очевидное название.
В любом случае, фича полезная. Фактически это некий хук, который срабатывает, когда элемент рендерится в DOM. Для этого введена новая директива use:
И соответствующая секция в поведении:
Экшн — это функция, которая принимает первым параметром элемент, к которому применена директива, и данные, которые были переданы в нее. Функция должна вернуть объект с обязательным методом destroy(), который будет вызван в тот момент, когда элемент будет удален из DOM. Также объект может содержать не обязательный методом update(), который будет вызываться каждый раз, когда связанные с экшеном данные были изменены.
В общем, если есть необходимость производить манипуляции с DOM напрямую, мимо реактивности Svelte, экшены позволяют делать это удобно и дают механизм синхронизации этих изменений.
В предыдущей версии были лишь 2 хука: oncreate() и ondestroy(). Теперь мы имеем также 2 дополнительных хука, отвечающих за работу с состоянием:
Как видите, каждый хук принимает объект с 3-мя свойствами:
Иcпользовать можно так:
Или даже так:
В связи с этим важным изменением метод observe() был вынесен из ядра в пакет дополнений svelte-extras. Поэтому если нравится предыдущий синтаксис, можно просто подключить соответствующий метод из этого пакета:
Если вспомнить что Рич, как создатель Rollup, является фанатом tree-shaking'а, такой подход сразу становится очевидным.

Да, знаю, это подсмотрели у JSX, но сути это не меняет. Многие проголосовали ЗА и теперь Svelte умеет также:
Немаловажные изменения произошли и в некоторых существующих api фреймворка. Вот основные из них:
Было
Стало
Это прикольно и мы можем использовать деструктурирующее присваивание для определения необходимых свойств. К тому же, теперь данный метод стал больше похож на своего антагониста, метод set(), который еще в предыдущей версии принимал исключительно объект:
Вообще у меня создается впечатление, что Svelte все больше склоняется к использованию RORO в своих интерфейсах. А полный переход на ES6 лишь способствует этому.
Было
Стало
На первый взгляд странное и не слишком полезное изменение (разве что RORO), но на самом деле следующим шагом будет возможность создания вычисляемого свойства зависящего от всего стейта компонента. Например, для его фильтрации или иных манипуляций, а также передачи в дочерние компоненты с помощью spread-аттрибута, примерно таким образом (пока это не работает):
Думаю многие понимают насколько это круто. Надеюсь Рич запилит эту фичу в ближайшее время.
Было
Стало
Теперь нужно явно указывать тип отличный от строки с помощью выражения. Более всего это касается чисел:
Было
Стало
Думаю смысл понятен. Мудрое решение.
Было
Стало
В предыдущей версии через префикс $ были доступны только данные из стора.

Для наглядности накатал свою собственную «тудушечку». В ней я постарался отразить максимум новых возможностей Svelte, которые можно применить к данной задаче, опять же для наглядности.
Тудушечка умеет CRUD над задачами, эмуляцию асинхронного взаимодействия с персистентным стейтом (хранилищем, бекендом и т.п.) и querying'ом по одному параметру — типу todo-листа (work, family, hobby), а также легкими анимашками. Работает примитивно, пишется быстро. Все как я люблю ))))
→ Пощупать
Вот и все, всем спасибо! Хорошей пятницы и выходных!
Кому интересен Svelte и хотелось бы следить за его развитием — welcome в русскоязычный телеграм канал SvelteJS. Будем рады вам!

Если вдруг, по какой-то неведомой причине, вы не знаете что такое Svelte и почему это не «yet another javascript framework». Предлагаю сперва наверстать упущенное, чтобы лучше понимать о чем речь.
Новый синтаксис шаблонов
Самое очевидное и глобальное изменение в новой версии — кардинальная смена синтаксиса шаблонов. Рич наконец-то решил избавиться от «усо»-подобного синтаксиса в пользу более лаконичного варианта:
Было
{{#if foo}} {{bar}} {{else}} {{baz}} {{/if}}
Стало
{#if foo} {bar} {:else} {baz} {/if}
Очевидно что синтаксис стал визуально проще и чище. Изменения коснулись всех конструкций в шаблонах, в том числе специальных элементов Svelte:
Было
<:Component {foo ? Red : Blue} name="thing" /> {{#if foo}} <:Self /> {{/if}} <:Window on:keydown="handleKey(event)" /> <:Head> <title>{{post.title}} • My blog</title> </:Head>
Стало
<svelte:component this="{foo ? Red : Blue}" name="thing"/> {#if foo} <svelte:self/> {/if} <svelte:window on:keydown="handleKey(event)" /> <svelte:head> <title>{post.title} • My blog</title> </svelte:head>
Предыдущий синтаксис специальных элементов был уж слишком необычен и большинство редакторов кода не справлялись с его подсветкой. Новый синтаксис напоминает синтаксис namespace из XML и значительно лучше воспринимается редакторами.
Надо отдать должное Ричу и отдельно отметить, что все изменения активно обсуждались с сообществом, а некоторые мои предложения вошли в финальный вариант синтаксиса и, вроде как, даже способствовали упрощению парсера.
В общем изменений в синтаксисе много и это могло бы стать проблемой для миграции на новую версию, если бы не утилита svelte-upgrade, специально созданная для автоматического апгрейда Svelte-компонентов. Полный список изменений, можно посмотреть там же.
Годно, Рич! Прощайте «усы»!

ES6 only
Так как Svelte — это прежде всего компилятор, стоит сначала отметить, что итоговый код предыдущей версии компилировался в ES5. Поэтому для поддержки IE11 и других «прогрессивных» версий браузеров, не было нужды связываться с транспиллерами вроде Babel или Bublé.
Но на дворе 2018-й и чтобы компилятор мог продуцировать более оптимальный и еще более компактный код, было принято решение отказаться от поддержки ES5. Иными словами, теперь Svelte компилирует компоненты в ES6 и нам придется использовать транспиллер, если необходима поддержка старых версий.
Сам я полностью поддерживаю такой подход. Тем более что подключить Babel к Webpack или Rollup, уверен, ни для кого уже не составит труда. Особенно если учесть, что использовать Svelte без оных все равно не получится. ;-)
Actions
До сих пор не понимаю почему эта фича называется actions, но для себя решил, что носителям языка виднее. Хотя лично для меня — это не очевидное название.
В любом случае, фича полезная. Фактически это некий хук, который срабатывает, когда элемент рендерится в DOM. Для этого введена новая директива use:
<img src="placeholder.jpg" use:lazyload="{ src: 'giant-photo.jpg' }">
И соответствующая секция в поведении:
export default { actions: { lazyload(node, data) { // do something return { update(data) {}, destroy() {} } } } };
Экшн — это функция, которая принимает первым параметром элемент, к которому применена директива, и данные, которые были переданы в нее. Функция должна вернуть объект с обязательным методом destroy(), который будет вызван в тот момент, когда элемент будет удален из DOM. Также объект может содержать не обязательный методом update(), который будет вызываться каждый раз, когда связанные с экшеном данные были изменены.
В общем, если есть необходимость производить манипуляции с DOM напрямую, мимо реактивности Svelte, экшены позволяют делать это удобно и дают механизм синхронизации этих изменений.
Новые хуки жизненного цикла
В предыдущей версии были лишь 2 хука: oncreate() и ondestroy(). Теперь мы имеем также 2 дополнительных хука, отвечающих за работу с состоянием:
export default { onstate({ changed, current, previous }) { // вызывается до oncreate(), и каждый раз, когда состояние изменилось }, onupdate({ changed, current, previous }) { // вызывается после oncreate(), и каждый раз, когда DOM был обновлен после изменения состояния } };
Как видите, каждый хук принимает объект с 3-мя свойствами:
- changed — включает в себя ключи, которые были изменены в стейте. Используется для проверки
- current — измененный стейт
- previous — предыдущий стейт
Иcпользовать можно так:
export default { onstate({ changed: { foo }, current, previous }) { if (foo) { console.log('foo has changed from %s to %s', previous.foo, current.foo); } } };
Или даже так:
component.on('state', ({ changed, current, previous }) => {...});
В связи с этим важным изменением метод observe() был вынесен из ядра в пакет дополнений svelte-extras. Поэтому если нравится предыдущий синтаксис, можно просто подключить соответствующий метод из этого пакета:
import { observe } from 'svelte-extras'; export default { methods: { observe }, oncreate() { this.observe('foo', (current, previous) => {...}); } };
Если вспомнить что Рич, как создатель Rollup, является фанатом tree-shaking'а, такой подход сразу становится очевидным.

Spread attributes
Да, знаю, это подсмотрели у JSX, но сути это не меняет. Многие проголосовали ЗА и теперь Svelte умеет также:
<Child {...childProps} />
Другие изменения
Немаловажные изменения произошли и в некоторых существующих api фреймворка. Вот основные из них:
Метод get() больше не принимает параметры и всегда возвращает весь стейт компонента:
Было
const foo = this.get('foo'); const bar = this.get('bar');
Стало
const { foo, bar } = this.get();
Это прикольно и мы можем использовать деструктурирующее присваивание для определения необходимых свойств. К тому же, теперь данный метод стал больше похож на своего антагониста, метод set(), который еще в предыдущей версии принимал исключительно объект:
this.set({ foo: 1 }); const { foo } = this.get();
Вообще у меня создается впечатление, что Svelte все больше склоняется к использованию RORO в своих интерфейсах. А полный переход на ES6 лишь способствует этому.
Это же наблюдение подтверждает новый синтаксис вычисляемых свойств:
Было
export default { computed: { d: (a, b, c) => a = b + c } };
Стало
export default { computed: { d: ({ a, b, c }) => a = b + c } };
На первый взгляд странное и не слишком полезное изменение (разве что RORO), но на самом деле следующим шагом будет возможность создания вычисляемого свойства зависящего от всего стейта компонента. Например, для его фильтрации или иных манипуляций, а также передачи в дочерние компоненты с помощью spread-аттрибута, примерно таким образом (пока это не работает):
<Child {...props}/> <script> import Child from './Child.html'; export default { components: { Child }, computed: { props: state => { const { unwanted, alsoUnwanted, ...props } = state; return props; } } }; </script>
Думаю многие понимают насколько это круто. Надеюсь Рич запилит эту фичу в ближайшее время.
Обработчики кастомных ивентов теперь должны возвращать destroy() вместо teardown() для консистентности:
Было
export function eventHandler(node, callback) { //... return { teardown() {} } }
Стало
export function eventHandler(node, callback) { //... return { destroy() {} } }
Svelte больше не приводит значения аттрибутов компонентов к типу
Теперь нужно явно указывать тип отличный от строки с помощью выражения. Более всего это касается чисел:
Было
<Counter start="1"/>
Стало
<Counter start="1"/> <!-- строка --> <Counter start="{1}"/> <!-- число -->
Думаю смысл понятен. Мудрое решение.
В шаблонах методы стора теперь можно вызывать через префикс $.
Было
<button on:click="store.set({ clicked: true })">click me</button>
Стало
<button on:click="$set({ clicked: true })">click me</button>
В предыдущей версии через префикс $ были доступны только данные из стора.
Тудушечка

Для наглядности накатал свою собственную «тудушечку». В ней я постарался отразить максимум новых возможностей Svelte, которые можно применить к данной задаче, опять же для наглядности.
Тудушечка умеет CRUD над задачами, эмуляцию асинхронного взаимодействия с персистентным стейтом (хранилищем, бекендом и т.п.) и querying'ом по одному параметру — типу todo-листа (work, family, hobby), а также легкими анимашками. Работает примитивно, пишется быстро. Все как я люблю ))))
→ Пощупать
Вот и все, всем спасибо! Хорошей пятницы и выходных!
UPDATE:
Кому интересен Svelte и хотелось бы следить за его развитием — welcome в русскоязычный телеграм канал SvelteJS. Будем рады вам!
