Хабрахабр

SvelteJS: Релиз второй версии

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

Новый синтаксис шаблонов

Самое очевидное и глобальное изменение в новой версии — кардинальная смена синтаксиса шаблонов. Рич наконец-то решил избавиться от «усо»-подобного синтаксиса в пользу более лаконичного варианта:

Было

} {{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-компонентов.

Прощайте «усы»! Годно, Рич!

image

ES6 only

Так как Svelte — это прежде всего компилятор, стоит сначала отметить, что итоговый код предыдущей версии компилировался в ES5. Поэтому для поддержки IE11 и других «прогрессивных» версий браузеров, не было нужды связываться с транспиллерами вроде Babel или Bublé.

Иными словами, теперь Svelte компилирует компоненты в ES6 и нам придется использовать транспиллер, если необходима поддержка старых версий. Но на дворе 2018-й и чтобы компилятор мог продуцировать более оптимальный и еще более компактный код, было принято решение отказаться от поддержки ES5.

Тем более что подключить 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), а также легкими анимашками. Все как я люблю ))))

→ Пощупать

Хорошей пятницы и выходных! Вот и все, всем спасибо!

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»