- PVSM.RU - https://www.pvsm.ru -

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

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

Если вдруг, по какой-то неведомой причине, вы не знаете что такое Svelte и почему это не «yet another javascript framework». Предлагаю сперва наверстать [3] упущенное [4], чтобы лучше понимать о чем речь.

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

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

Было

{{#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 и значительно лучше воспринимается редакторами.

Надо отдать должное Ричу и отдельно отметить, что все изменения активно обсуждались [5] с сообществом, а некоторые мои предложения [6] вошли в финальный вариант синтаксиса и, вроде как, даже способствовали упрощению парсера.

В общем изменений в синтаксисе много и это могло бы стать проблемой для миграции на новую версию, если бы не утилита svelte-upgrade [7], специально созданная для автоматического апгрейда Svelte-компонентов. Полный список изменений, можно посмотреть там же [8].

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

image

ES6 only

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

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

Сам я полностью поддерживаю такой подход. Тем более что подключить Babel к Webpack [9] или 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 [10]. Поэтому если нравится предыдущий синтаксис, можно просто подключить соответствующий метод из этого пакета:

import { observe } from 'svelte-extras';

export default {
  methods: { observe },
  oncreate() {
    this.observe('foo', (current, previous) => {...});
  }
};

Если вспомнить что Рич, как создатель Rollup [11], является фанатом tree-shaking'а [12], такой подход сразу становится очевидным.

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

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 [13] в своих интерфейсах. А полный переход на ES6 лишь способствует этому.

Это же наблюдение подтверждает новый синтаксис вычисляемых свойств:

Было

export default {
  computed: {
    d: (a, b, c) => a = b + c
  }
};

Стало

export default {
  computed: {
    d: ({ a, b, c }) => a = b + c
  }
};

На первый взгляд странное и не слишком полезное изменение (разве что RORO), но на самом деле следующим шагом [14] будет возможность создания вычисляемого свойства зависящего от всего стейта компонента. Например, для его фильтрации или иных манипуляций, а также передачи в дочерние компоненты с помощью 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>

В предыдущей версии через префикс $ были доступны только данные из стора.

Тудушечка

SvelteJS: Релиз второй версии - 4
Для наглядности накатал свою собственную «тудушечку». В ней я постарался отразить максимум новых возможностей Svelte, которые можно применить к данной задаче, опять же для наглядности.

Тудушечка умеет CRUD над задачами, эмуляцию асинхронного взаимодействия с персистентным стейтом (хранилищем, бекендом и т.п.) и querying'ом по одному параметру — типу todo-листа (work, family, hobby), а также легкими анимашками. Работает примитивно, пишется быстро. Все как я люблю ))))

Пощупать [15]

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

Автор: PaulMaly

Источник [16]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/278328

Ссылки в тексте:

[1] вышла: https://github.com/sveltejs/svelte/releases/tag/v2.0.0

[2] SvelteJS: https://svelte.technology/

[3] наверстать: https://habrahabr.ru/post/345028/

[4] упущенное: https://habrahabr.ru/post/345208/

[5] активно обсуждались: https://github.com/sveltejs/svelte/issues/1318

[6] мои предложения: https://github.com/sveltejs/svelte/issues/1318#issuecomment-378982102

[7] svelte-upgrade: https://github.com/sveltejs/svelte-upgrade

[8] там же: https://github.com/sveltejs/svelte-upgrade#svelte-v2-syntax-changes

[9] Babel к Webpack: https://www.valentinog.com/blog/webpack-4-tutorial/#webpack_4_transpiling_Javascript_ES6_with_Babel

[10] svelte-extras: https://github.com/sveltejs/svelte-extras

[11] Rollup: https://rollupjs.org/guide/en

[12] tree-shaking'а: https://rollupjs.org/guide/en#tree-shaking

[13] RORO: https://medium.freecodecamp.org/elegant-patterns-in-modern-javascript-roro-be01e7669cbd

[14] следующим шагом: https://github.com/sveltejs/svelte/issues/1303#issuecomment-381363853

[15] Пощупать: https://svelte.technology/repl?version=2.0.0&gist=8439585e9574d93409cd292ed29d68ee

[16] Источник: https://habrahabr.ru/post/353896/?utm_campaign=353896