https://habrahabr.ru/post/353896/- Разработка веб-сайтов
- Клиентская оптимизация
- JavaScript
- HTML
Буквально вчера
вышла 2-я версия молодого, но весьма многообещающего фреймворка
SvelteJS. Версия мажорная, а значит содержит не только новые фичи и исправленные баги, но и соответствующие «breaking changes». Что новенького предлагает разработчикам новая версия и почему
Svelte стал еще лучше, читайте под катом.
Если вдруг, по какой-то неведомой причине, вы не знаете что такое
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), а также легкими анимашками. Работает примитивно, пишется быстро. Все как я люблю ))))
→
Пощупать
Вот и все, всем спасибо! Хорошей пятницы и выходных!