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

Если вдруг, по какой-то неведомой причине, вы не знаете что такое 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-компонентов. Полный список изменений, можно посмотреть там же.

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

image

ES6 only


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

Но на дворе 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), а также легкими анимашками. Работает примитивно, пишется быстро. Все как я люблю ))))

> Пощупать

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

Комментарии (57)


  1. jashcka
    20.04.2018 19:06

    а некоторые еще говорят что шаблоны Angular с кучей символов ))
    вот где можно все символы в шаблон засунуть (Svelte) )


    1. PaulMaly Автор
      20.04.2018 19:11

      Не сказал бы что Svelte многословнее в шаблонах, чем тот же Vue, Ractive, ну и Angular, да. Опять же смотря как писать. Просто я чаще всего использую своеобразный «view driven development», т.е. иду от потребителя (вьюха). Поэтому иногда у меня императивщины больше в шаблонах, чем в скриптах, которые выходят весьма декларативными.


    1. MadLord
      21.04.2018 07:37
      -2

      Да, открыл статью, увидел синтаксис, закрыл статью.


      1. vintage
        21.04.2018 09:02
        +1

        Вы поступили крайне непрофессионально. Не стоит об этом хвастаться в приличном обществе.


      1. PaulMaly Автор
        21.04.2018 10:25
        +1

        Хорошо хоть не забыли в комментарии отписаться ;-) Судя по комментариям ниже, хорошим синтаксисом вы считаете JSX? Ну чтож, это ваше право. Думаю найдется как много сторонников, там и много противников этого синтаксиса.


        1. MadLord
          21.04.2018 10:48

          Нет, у каждого синтаксиса есть свои слабые стороны. Речь велась о конкретном


          1. PaulMaly Автор
            21.04.2018 10:54

            Речь? Мне показалось это не более чем ваше мнение. Лично для меня псевдо-js в js значительно менее очевидная вещь, но я и не отношу себя к любителям реакт.


  1. bgnx
    21.04.2018 00:07

    Меня больше всего удивляет синтаксис этого фреймворка


    {#each todos as todo, i (todo.id)}
    <li class="{todo.done && 'checked'}">
      <input type="checkbox" bind:checked="todo.done">
      <input class="inline-input" bind:value="todo.body">
    
      {#if todo.nextSibling}
        <div>
        > {todo.nextSibling.text}
        <div>
      {/if}
    </li>
    {/each}

    Объясните пожалуйста как этот синтаксис типизируется? Как typescript или flow могут проверить что я не опечатался и в bind:checked="todo.done" идет обращение к объекту todo из массива todos у которого есть свойство "done"? Или как обращение свойству "nextSibling" которое имеет тип "null | todo" будет безопасным после проверки и чтобы без проверки компилятор надавал по рукам? Вот в реакте есть киллер-фича — в нем все байндинги (экспрешены, условия, циклы) прекрасно типизируются и это значительно упрощает жизнь разработчику но тем не менее некоторые все равно продолжают выдумывать фреймворки построенные на шаблонах с кастомным синтаксисом и спец-аттрибутами (и весь этот синтаксис еще надо дополнительно изучать новичку помимо знания js) вместо того чтобы условия и циклы реализовать нативным js-синтаксисом как в реакте в виде


    {todos.map(todo=>(
     <li class={todo.done ? "checked" : ""} >
      <input type="checkbox" checked={todo.done}>
      <input class="inline-input" value={todo.body}>
       {todo.nextSibling && (
         <div>
          >  {todo.nextSibling.text}
         </div>)}
    </li>)}


    1. faiwer
      21.04.2018 07:39

      bgnx, это была ирония? :) Или вы на полном серьёзе? Ну тогда вот:


      ? по мнению многих, в том числе и моему, JSX отвратителен. И в первую очередь из-за всех этих "хаков", которым в JSX не нашлось нормального сахара: ?:, &&, .map(el => и пр… Учитывая, что код так и так транспайлится, то совершенно непонятно, почему нельзя было улучшить язык внедрив туда нормальную поддержку вветвлений и циклов. Лично я спасаюсь за счёт jsx-control-statements.
      ? большинство не использует ни TS, ни Flow. Предъявлять к JS библиотеке требования TS это ну… как минимум грубо по отношению к её авторам. Да ещё и в таком виде.


      1. MadLord
        21.04.2018 07:54
        +1

        Многих? Это откуда такая статистика? Сравните на гитхабе статистику ангуляра и реакта — вот статистика. Я пишу на React+TS и так называемые «хаки», в отличие от странных ngFor, есть чистый JS и гораздо понятнее.


        1. PaulMaly Автор
          21.04.2018 10:55
          +1

          JSX — это не чистый JS, и даже не валидный HTML. Не нужно тиражировать мифы, пожалуйста.


        1. faiwer
          21.04.2018 17:43

          Сравните на гитхабе статистику ангуляра и реакта

          А причём тут статистика React-а? Ей богу, ну что за аргументы. React это куда больше, чем JSX. Если уж на то пошло, то React это вообще не JSX. Не путайте мягкое с тёплым. Да и статистика к обсуждаемому вопросу имеет отношение чуть менее, чем никакое.


          Спорить про читаемость синтаксиса не буду, ибо на вкус и цвет все фломастеры разные. Меня смутило именно то, что ваша агрессивная позиция, дескать всё, что не JSX есть зло, всё что не TS есть зло и пр. Указания про то как автору надо было сделать и пр… Грубо, нелепо и не профессионально. Фи


          1. faiwer
            21.04.2018 18:01

            Пардон, имел ввиду позицию bgnx. Но, кажется, у вас такая же. Особенно глядя на это.


      1. MadLord
        21.04.2018 07:56

        Вообще не использование TS в React в большой команде это ИМХО боль.


      1. bgnx
        21.04.2018 10:44

        faiwer, интересно а как тот же jsx-control-statements работает с тайпскриптом или флоу — разве он не будет ругаться на необъявленную переменную item в этом примере


        <For each="item" of={ this.props.items }>
            <span key={ item.id }>{ item.title }</span>
          </For>

        и сможет проверить наличие нужных свойств id и title у item? Да, можно сначала скомпилировать шаблон в js и потом проверить но даже если настроить вотчер время отклика будет далеко от юзабельного. И получается что суть этих jsx-хаков ( ?:, &&, .map(el=>)) не сколько в каком-то в том визуальном удобстве или даже не в отсутствии необходимости учить кастомный синтаксис а в банальной возможности протипизировать шаблон


        1. faiwer
          21.04.2018 17:58

          Оно компилируется в JS. Это просто шаг транспайлера в babel. Так что всё упирается в ваш toolkit. Умеет он нестандартные расширения языка (вроде JSX) или нет. Я с TS не работал, и не знаю как его точно готовят, но в JS оно встраивается хорошо. Скажем есть плагин для eslint и c ним item это настоящая JS-переменная (не TS), index тоже. Это плагин к Babel, а не к TS.


          Топик про JS библиотеку. Не про TS библиотеку. Не надо относиться к TS, как к данности. И тем более писать такое:


          все равно продолжают выдумывать фреймворки построенные на шаблонах с кастомным синтаксисом и спец-аттрибутами

          Скажем JSX это кастомный синтаксис. Там всё кастомное. Это не JavaScript. И есть вероятность того, что JSX никогда им не станет. Ключевая разница в том, что это очень известная библиотека, поддержка которой вшита в ряд IDE и прочих редакторов в той или иной степени. Попытки обвинять другие/новые/старые библиотеки в том, что для них нет готового транспайлера и широкой поддержки в IDE выглядят… ну сами догадайтесь. Вы ещё обвините сторонние либы в том, что за ними не стоят Facebook, Google, Amazon, etc. Ваше право оценивать нужна вам та или иная библиотека, или не нужна. Но такие претензии выглядят дико. Фанбойство какое-то.


          P.S. я использую React + Redux. Я не использую TS. Я много что могу на эту тему рассказать и показать. Но мне и в голову бы не пришло предъявлять какие-то претензии в таком ключе авторам библиотек, которым, скажем, даже не нужен транспайлер, в которых есть какой-нибудь DSL, которые предлагают подходы несовместимые со строгой типизацей и пр… Это разные подходы, со своими плюсами и минусами.


          P.S.S. мне за мой стаж неоднократно приходилось писать DSL, даже на шаблоно-строках из ES7. Естественно там не будет работать магия IDE. И что?


        1. apapacy
          21.04.2018 18:37

          Один из хороших свойств реакта это как раз информативные сообщения об ошибках. Мне как-то пришлось очень быстро за пару дней переводить один проект с бэкбон/марионетт на серверный рендеринг (из соображений СЕО) и я заюзал riot т.к. реально можно было за несколько часов изучить и за два дня перевести. Но вот с поиском ошибко была очеь большая трабла. Вдруг вылетает сообщение об ошибка в библиотеке riot.js cj без всяких там привязок к тэгам и ищи где хочешь. После этого на реакте просто отдыхал. Любая неточность и у тебя уже и в консоли и в браузере высвечивается ошибка с полным стеком и т.п.


    1. PaulMaly Автор
      21.04.2018 10:45
      +1

      Хороший вопрос, спасибо. Для начала, с чего вы взяли, что в компонентах Svelte можно использовать Typescript или Flow?))))

      Сам компилятор Svelte написан на Typescript, но в то же время Svelte — это еще статический анализатор кода. Поэтому если автор решит, что компонентам Svelte следует строже подходить к типизации, это можно будет реализовать без проблем. А в глубоком рантайме у вас ни Typescript, ни Flow все равно нет.

      Далее, если вы читали статью, то заметили что Svelte использует SFC, что само по себе DSL. И еще вы заблуждаетесь, JSX — это не нативный js. Это не более чем миф. Это такой же DSL как и остальные. Более того, JSX не только не валидный JS, но и не валидный HTML. Своеобразный «чудоюдорыбакит» и это весьма путает при работе с ним.

      А вообще не очень понятно, как типизация связана с синтаксисом? Вот глядите:

      <div>{ /* все что между скобками это обычные js выражения*/ }</div>
      <div>{ JSON.stringify(data) }</div>
      

      Как таковых специальных конструкций там не много.


      1. bgnx
        21.04.2018 11:01
        +2

        JSX — это не нативный js. Это не более чем миф. Это такой же DSL как и остальные. Более того, JSX не только не валидный JS, но и не валидный HTML.

        Суть не в самом jsx а в использовании js для условий или циклов чтобы тайпскрипт или flow могли тайпчекать шаблоны. Помимо jsx это может быть подход шаблонных строк как в lit-html фреймвоке


        h`<div>
        ${todos.map(todo=>h`
         <li class=${todo.done ? "checked" : ""} >
          <input type="checkbox" checked=${todo.done}>
          <input class="inline-input" value=${todo.body}>
           ${todo.nextSibling && 
             h`<div>
              >  ${todo.nextSibling.text}
             </div>`}
         </li>`}
        </div>`

        Который уж точно на 100% нативный и няшный js и нам даже не нужно для запуска настраивать babel для компиляции "ужасного" jsx и мы получаем возможность полного тайпчекинга шаблонов


        1. PaulMaly Автор
          21.04.2018 16:59
          +1

          Все верно, но давайте ещё раз…

          Svelte — это в том числе статический анализатор кода, поэтому ничего не мешает ему тайпчекать код компонентов и без необходимости осваивать Typescript или подключать Flow. А главное все никак вообще не завязано на синтаксис. Понимаете?


  1. drai3
    22.04.2018 14:06

    Спасибо за статью. После прочтения статьи и комментариев у меня осталось 2 вопроса и, надеюсь, вы сможете их прояснить для меня)
    Первый вопрос: все-таки почему же это не «yet another javascript framework»". Какие проблемы он решает лучше чем React/Angular/Vue?
    Второй: Я прекрасно понимаю что JSX это не JavaScript, но чем синтаксис Svelte лучше? Чем вы мотивируете разработчиков учить новый синтаксис нового фреймворка, который не работает (или плохо работает) в многих IDE. Разумно предположить что он как минимум должен быть лучше JSX или подходов Angular/Vue. Возможно были какие-то объективные причины выбора именно такого синтаксиса?


    1. PaulMaly Автор
      22.04.2018 15:41

      Пожалуйста, спасибо за вопросы. Постараюсь ответить по существу.

      Первый вопрос: все-таки почему же это не «yet another javascript framework»".

      Пожалуй, цель данной статьи больше информационная — сообщить о выходе 2-й версии. Поэтому согласен, вряд ли вы бы уловили разницу на ее основе. Именно поэтому, в связке с моим утверждением по поводу того, что Svetle не «yet another javascript framework», удут ссылки на две предыдущие статьи на тему Svelte: Магически исчезающий JS фреймворк и 1Kb autocomplete. Подразумевалось что именно их прочтение и даст вам ответ почему это так. Поправьте меня, если ошибаюсь, но полагаю вы не прочли их.

      Краткий ответ на вопрос: Svelte не «yet another javascript framework» потому что это вообще не фремворк, в привычном понимании. Это статический анализатор кода + компилятор компонентов написанных в формате SFC в ванильный JS без зависимостей на специфический рантайм. Иными словами, Svelte buildtime framework, которого нет в вашем runtime. Можно еще выразиться так: Svelte — это способ писать vanilla-js приложения (потому что важен «performance») без необходимости писать на vanilla-js (потому что важно избегать «complexity»).

      Какие проблемы он решает лучше чем React/Angular/Vue?

      Например, такие проблемы как: скорость работы, потребляемая памяти, время запуска, размер бандла, простота разработки.

      Вот benchmarks (v1.58.5 версия Svelte довольно старая, сейчас полагаю может еще лучше). Если обратите внимание, то в keyed-results Vue чуть быстрее, а в non-keyed на удивление Angular 5, но если брать все метрики сразу (скорость, старт, память), то в сравнении Svelte/Vue/Angular/React, только Svelte имеет абсолютно «зеленую» колонку.

      Вот некоторая статистика во размеру бандла:
      ToDoMVC: Svelte (3,6kb), React (300kb!!! шок, сам не знаю почему!!!), Vue (80kb), Vanilla (11kb)
      RealWorld: Svelte (40kb), React/Redux (193kb), Angular (317kb), Vue (100kb)
      HNpwa: Svelte (13kb), React (42kb), Vue (101kb)

      Многие скажут, что все эти бенчмарки — это ерудна и, я даже соглашусь, но тогда и постановка вопроса «кто и чем лучше» такая же ерунда. К тому же других данных у нас все равно нет.)))

      Второй: Я прекрасно понимаю что JSX это не JavaScript, но чем синтаксис Svelte лучше? Чем вы мотивируете разработчиков учить новый синтаксис нового фреймворка, который не работает (или плохо работает) в многих IDE. Разумно предположить что он как минимум должен быть лучше JSX или подходов Angular/Vue. Возможно были какие-то объективные причины выбора именно такого синтаксиса?

      Что-то в комментах к этой статье все прям зациклились на синтаксисе. Вообще причем тут JSX? Я разве где-то занимался сравнением или каким-то образом дал повод так думать? Нравится вам JSX — пожалуйста, никто вам ничего не навязывает. Для меня ахилесова пята JSX берет свои корни ровно из тех плюсов, которые все ему вменяют, мол это очень похоже на JS и поэтому редакторы, типы и все такое.

      JSX очень опасен и не очевиден тем, что это и не валидный JS, так и не валидный HTML, на чем часто попадаются новички. Пару примеров:

      Valid HTML, invalid JSX
      <img src="logo.png">
      
      <svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
      <!-- something -->
      </svg>
      

      Valid JSX, но не делает то, что я ожидаю
      <button style="display:none;">hidden btn</button>
      <div class="foo">...</div>
      

      Ты смотришь на разметку JSX — выглядит как HTML, пахнет как HTML, но это не HTML. Смотришь на код JSX — выглядит как JS, пахнет как JS, но это не JS. Все это лишь путает.

      Для меня хороший DSL, который «domain» именно потому, что точно «заточен» под задачу, всегда лучше, чем попытка скрестить слона с хомяком, потому что при этом всегда получается «слонохомяк» и никогда не знаешь откуда и что у него вылетит. Надеюсь мысль по поводу JSX понятна.

      Далее, давайте сразу вынесем за скобки Angular, потому что в сравнении Svelte/Vue/React/Angular уж точно есть лишний. По поводу Vue история такая: Рич Харрис, создатель Svelte, еще в 2012 году придумал Ractive, там же он придумал virtual dom (назывался он parallel dom, 2012), server-side rendering (toHTML() метод, 2013) и component styles (css option, 2014), single file components (component-spec, 2014) и многие другие знакомые вам сейчас вещи. Vue кстати появился только в 2014. Это я на тему чьи это подходы в итоге.

      Ну а вообще, еще по первой части моего комментария, вы, я уверен, поняли, что сравнивать подход Svelte и Vue довольно сложно. Хотя визально SFC и там и там сильно похожи, но по-сути это разные вещи.


      1. PaulMaly Автор
        22.04.2018 15:49

        Вот еще в догонку забавная интерактивная писькомерка.


      1. drai3
        22.04.2018 20:31

        Спасибо, теперь суть фреймворка ясна намного лучше и результаты бенчмарков очень впечатляют. И всё же мне все равно не ясен ответ на мой второй вопрос. Возможно, я недостаточно точно его сформулировал. Попробую расписать подробнее.


        В чем смысл делать такой синтаксис?


        <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>

        Если сравнить с таким же JSX:


        <Component this={foo ? Red : Blue} name="thing"/>
        
        {foo && <Self/>}
        
        <Window onCilck={handleKey} />
        
        <Head>
            <Title>{post.title} • My blog</Title>
        </Head>

        Мне, как разработчику, совершенно не хочется пробовать работать с фреймворком, синтаксис которого избыточен, не поддерживается моим IDE и не дает мне никаких преимуществ.


        JSX очень опасен и не очевиден тем, что это и не валидный JS, так и не валидный HTML, на чем часто попадаются новички

        Ваш синтаксис точно такая же "попытка скрестить слона с хомяком", только по-другому:


        {#each todos as todo, i (todo.id)}

        Это и не валидный js и не валидный xml. Это «слонохомяк» абсолютной такой же степени как и JSX, но с намного меньшим количеством примеров и информации в гугле. Начинающий разработчик может очень быстро нагуглить в чем его проблема и почему display не работает в вашем примере с JSX.


        И вот собственно мой вопрос: Какой смысл в использовании нового "слонохомяка", когда есть старые и проверенные шаблоны, к которым привыкли другие разработчики? С моей, возможно неправильной точки зрения, можно было бы использовать JSX, vue-like или angular-like синтаксис, а не изобретать новый велосипед, который только отталкивает разработчиков, как я, от использования svelte


        1. PaulMaly Автор
          22.04.2018 22:33

          Если сравнить с таким же JSX:

          Вы не можете сравнивать этот синтаксис с JSX и React, ведь в React нет аналогов вообще. Аналог есть пожалуй только у элемента <svelte:component />.

          По всей видимости, вы не поняли этой части статьи, а также все еще не ознакомились с другими статьями (там есть упоминания спец. элементов). Отсюда и вопросы. Прощу перечитать еще раз вводную, ключевое я на этот раз выделил:
          Изменения коснулись всех конструкций в шаблонах, в том числе специальных элементов Svelte:

          Подробнее про специальные элементы Svelte.

          Мне эта часть синтаксиса, пожалуй, нравится меньше всего и я предлагал заметить эту нотацию на более упрощенную, но для того, чтобы явно отделить предустановленные элементы (теги), от тегов компонентов, Рич принял решение использовать синтаксис из XML Namespaces. Это решение имеет под собой основания и я не могу винить его в плохом выборе. К тому же, как показал мой редактор, XML Namespaces вполне себе корректно подсвечивается.
          Мне, как разработчику, совершенно не хочется пробовать работать с фреймворком, синтаксис которого избыточен, не поддерживается моим IDE и не дает мне никаких преимуществ.

          Честно говоря, если бы вы действительно хотели бы разобраться, то хотя бы открыли бы мою демку и увидели бы там, что теги кастомных компонентов имеют очень похожий на React синтаксис:
          	<TodoList {todos} on:changed="saveTodos(event)" />
          

          Это и не валидный js и не валидный xml. Это «слонохомяк» абсолютной такой же степени как и JSX, но с намного меньшим количеством примеров и информации в гугле. Начинающий разработчик может очень быстро нагуглить в чем его проблема и почему display не работает в вашем примере с JSX.

          Вы не правы в этом. Это совершенно разные вещи. JSX — это «слонохомяк», потому что он создан из хомяка (JS) и слона (HTML), а что это за синтаксис?

          {#each todos as todo, i (todo.id)}
          

          Правильно, вы не знаете. Именно поэтому не предъявляете к нему изначально никаких требований, не имеете на его счет никаких умозаключений и т.д. Это определенный DSL — язык предметной области, хорошо под нее заточенный, удобно ложащийся в свой «domain» (в данном случае SFC, а точнее HTML). Вы не можете назвать его «хомяком» или «слоном», потому что он не похож на них. Вы можете назвать его как-то по-новому, например, «бинго-бонго» и изучить его возможности и будете точно знать что он умеет, а что нет. Это очень большая разница.

          Ну и язык заточенный под «domain» это всегда лучше:
            const todos = props.todos;
            const todosItems = todos.map((todo) =>
              <TodoItem key={todo.id.toString()} todo={todo} />
            );
            return (
              <ul>
                {todosItems}
              </ul>
            );
          

          <ul>
          {#each todos as todo (todo.id)}
            <TodoItem todo={todo} />
          {/each}
          </ul>
          

          Примеры абсолютно идентичным, просто первый избыточен, так как js плохо читается в потоке html и наоборот. Второй прост и лаконичен.

          Какой смысл в использовании нового «слонохомяка», когда есть старые и проверенные шаблоны, к которым привыкли другие разработчики? С моей, возможно неправильной точки зрения, можно было бы использовать JSX, vue-like или angular-like синтаксис, а не изобретать новый велосипед, который только отталкивает разработчиков, как я, от использования svelte

          Эту статью вы видимо тоже не внимательно читали. Там черным по белому написано, что в предыдущей версии Svelte имел mustache (handlebars) — подобный синтаксис, а это чуть ли не самый распространенный синтаксис шаблонов и не только в JS: mustache.github.io

          Про JSX я все сказал выше. vue-like — это те же «усы» + angular-like (директивы). Но в новой версии Рич решил, что «усы» себя изжили, потому что идут корнями из строковых шаблонов, где использование одинарных скобок было чревато. В Svelte такой проблемы нет.

          Далее, сам формат компонентов SFC (single-file component), это как бы практически стандарт: Vue SFC, RiotJS, Ractive SFC.

          Давайте взглянем на структуру SFC в Svelte:
          <div class="big-text">
             Hello world
          </div>
          
          <script>...</script>
          
          <style>...</style>
          

          Что напоминает? Правильно, это HTML, причем совершенно валидный. Что вы в нем не знаете? Более того, даже присловутые Svelte-примочки не заставят его работать так, как вы не ожидаете. Именно этим и хорош DSL, он дополняет, а не ломает.


          1. drai3
            22.04.2018 22:59

            Спасибо за столь терпеливый и объемный ответ. Я обязательно перечитаю всё ещё раз и попробую глубже понять суть, но сразу хочу заметить, что ваш пример сравнения JSX с Svelte-синтаксисом совершенно нечестный


            Примеры абсолютно идентичным, просто первый избыточен, так как js плохо читается в потоке html и наоборот. Второй прост и лаконичен.

            в JSX-примере вы добавили 2 строчки, который нет в Svelte примере вначале и зачем-то сохранили все todo в переменную вместо вполне обычного мэпа внутри тэга ещё и добавив return. Правильное сравнение должно быть таким:


            <ul>
            {#each todos as todo (todo.id)}
              <TodoItem todo={todo} />
            {/each}
            </ul>

            против


            <ul>
                {todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
            </ul>

            ну или код полного компонента:


            const TodoList = ({ todos }) => (
            <ul>
                {todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
            </ul>
            )
            

            Глупо спорить какой код читается лучше — это довольно субъективно, но, как минимум, код JSX лаконичнее.


            1. PaulMaly Автор
              23.04.2018 00:08

              ваш пример сравнения JSX с Svelte-синтаксисом совершенно нечестный

              Честно говоря, давненько не писал на React. Пожалуй последний раз это было 2 года назад и тогда мы писали как-то так. Поэтому не претендую на оптимальность. Однако, даже этот пикантный момент показывает насколько DSL проще, ибо как-то по-другому на Svelte написать просто не получится. Единственное что можно сделать, это использовать или не использовать keyed-фичу, но в остальном всегда будет так как я написал.

              Глупо спорить какой код читается лучше — это довольно субъективно, но, как минимум, код JSX лаконичнее.

              Если говорить о лаконичности, вот, например, полный код условного компонента Stepper:

              <input value="{value}" type="number" readonly>
              <button on:click="set({ value: value - step })">-</button>
              <button on:click="set({ value: value + step })">+</button>
              

              А вот тот же компонент, только стилизованный как нам надо по средствам component-scoped стилей:

              <input value="{value}" type="number" readonly>
              <button on:click="set({ value: value - step })">-</button>
              <button on:click="set({ value: value + step })">+</button>
              
              <style>
                  /* стили тут */
              </style>
              

              Юзаем так:

              <Stepper step={2} value={0} />
              

              Предложите свой «более лаконичный» вариант на React?

              В продолжении темы стандартнов понятным всем и все такое. Могу вам доказать, что с отличии от компонента React, компонент Svelte — это валидный HTML и JS. Пишем компонент HelloWorld вообще без DSL:

              <div id="msg">Hello world!</div>
              <input id="input" value="Hello world">
              
              <script>
                import debounce from 'debounce';
              
                export default {
                  oncreate() {
              	const msg = document.getElementById('msg'),
              	   input = document.getElementById('input');
              	
              	input.addEventListener('keydown', debounce((e) => {
              	  msg.innerText = e.target.value;
              	}, 300));
              			
                  }
                };
              </script>
              
              <!-- используем -->
              <HelloWorld />
              


              И теперь еще одна из фишек Svelte о которой я писал в статьях, которые вы не читали, и которую точно не умеет ни один из сравниваемых фреймворков.

              К примеру, написано у вас приложение на React, но какой-то компонент тормозит. Так как Svelte не имеет зависимостей и тащит в свой бандл ровно тот функционал, который используется, то вы можете взять любой компонент Svelte и интегрировать его в ваше приложение React. На подобии того, как вы наверняка интегрируете какие-то сторонние ванильные либы.

              На примере компонента Stepper расширенного ивентами:

                 import SvelteStepper from './svelte_components/stepper.js'
               
                 const stepper = new SvelteStepper({
                        target: document.getElementById('stepper'),
                        data: {
                             value: 0,
                             step: 2
                        }
                 });
              
                 stepper.on('increment', (e) => {
                     /* do something */
                 });
                 stepper.on('decrement', (e) => {
                     /* do something */  
                 });
              



              1. drai3
                23.04.2018 03:14

                Ещё раз спасибо за объяснения, я ещё несколько коментов назад прочитал ваши первые статьи (и не только) про Svelte и понял как он работает (и я понимаю как работают все фреймвоки с SFC). Действительно получается что в некоторых синтаксис Svelte будет лаконичней JSX, в некоторых нет. Надеюсь попробую Svelte в будущем на практике.


              1. vintage
                23.04.2018 09:45

                Вот вы зря поленились написать стили, иначе бы обнаружили, что в разметке не хватает семантических классов у каждого элемента.

                Далее, лаконично у вас получается ровно до тех пор, пока состояние никого не интересует. Давайте сделаем что-то более реалистичное: у нас вне компонента есть число и нам нужно, чтобы компонент это число показывал и позволял его менять. Как лаконично описать такой компонент?


                1. PaulMaly Автор
                  23.04.2018 10:49

                  Мне не до конца ясны ваши консерны, но постараюсь ответить.

                  Вот вы зря поленились написать стили, иначе бы обнаружили, что в разметке не хватает семантических классов у каждого элемента.

                  Не понимаю почему наличие или отсутствие классов должно как-то повлиять на лаконичность? Особенно в сравнении с решением на том же React, ведь оно в таком случае также будет содержать эти классы. Правда, так как React из коробки не имеет component-scoped стилей, его реализция явно будет многословнее.

                  Более того, на самом деле, в Svelte мне не обязательно придумывать классы для таких маленьких компонентов, потому что Svelte все равно будет делать это. Да, его классы не семантические, что-то вроде svelte-j2n54k53, ну и что?

                  Давайте сделаем что-то более реалистичное: у нас вне компонента есть число и нам нужно, чтобы компонент это число показывал и позволял его менять. Как лаконично описать такой компонент?

                  Я видимо не понял задачу, но не вижу особой разницы между компонентом Stepper и этой задачей.

                  Вот вам рабочий пример: вне компонента Stepper, в компоненте App, есть число, хранящееся в свойстве «value», далее оно прокидиывается в Stepper, этот компонент его показывает (внутри поля input) и позволяет его менять с помощью кнопок. Компонент Stepper стилизован как-то, но при этом я даже классов не писал.

                  > REPL


                  1. vintage
                    23.04.2018 10:58

                    А при чём тут React? :-) Это типичная уловка — напишут хтмл, мол, смотрите как лаконично, а в реальном коде вся эта лаконичность обрастаят классами, идентификаторами и прочим непотребоством.


                    Значит двусторонний биндинг не требует специальное поддержки в компоненте? Хорошо. Но как он реализован? Два состояния, которые синхронизируются, как в Ангуляре? Или же состояние вложенного компонента выступает как прокси к состоянию внешнего? Или наоборот?


                    1. PaulMaly Автор
                      23.04.2018 11:17

                      React тут причем, потому что все познается в сравнении. А сравнивали мы по треду выше именно с React. Так же лаконичность она не в вакууме, а по сравнению с чем-то.

                      Двухстороннее связывание конечно же есть, хотя, так как есть еще и прекрасная система кастомных ивентов, можно делать по принципу «props down, events up» (для тех кто опасается 2way) и тоже не сложно будет. Даже не меняя компонент Stepper.

                      Вот пример: REPL. Еще раз, здесь двойное связывание не используется и Stepper компонент никак не меняется.

                      Про то как точно реализованы биндинги я не знаю и пока они работают мне не интересно)))


            1. faiwer
              23.04.2018 08:23

              но, как минимум, код JSX лаконичнее.

              Зависит от стиля:


              const TodoList = ({ todos }) => 
                <ul>
                  {todos.map(todo => 
                      <TodoItem 
                          key={todo.id} 
                          todo={todo} 
                      />)}
                </ul>

              Я бы написал скорее так ^. Читаемость значительно выросла (имхо), лаконичность исчезла. Читаемость всё равно ужасна, т.к. в XML синтаксис мы засунули груду } и ) и => и появились сложности с переносом строк (на мой взгляд в классическом JSX они нерешаемы, т.к. сложный JS очень плохо ложится на XML).


              Это я к чему? Не получится так в лоб оценить ни лаконичность, ни что бы то ни было ещё в случае JSX. Когда кодовая база становится большой — начинаешь калёным железом выжигать всё похожее на это:


              {todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}

              Перестаёшь экономить строки. Каждый аттрибут выносишь в отдельную строку. Все сложные JS-expressions высчитываешь в отдельных методах или выше return-а. И всё больше и больше тоскуешь по удобному indent-у, который в нормальном XML обычно вообще не проблема.


              В итоге компоненты читаются по диагонали легко. Не нужно парсить все скобки, разных типов, аргументы функций, искать аттрибуты слева направо и пр. Оно лежит в удобоваримом виде. В первую очередь из-за удобных отступов. Ну и финальный штрих это выжигание .map и && за счёт jsx-control-statement (на что не каждая команда согласится).


              По сути говоря, будь у вашей IDE поддержка Svetle синтаксиса на том же уровень, что и JSX, едва ли вы бы добрым словом вспомнили JSX.


              1. faiwer
                23.04.2018 08:26

                Из всех шаблонизаторов мне больше всего всегда симпатизировал Jade (который теперь Pug). Python | Ruby like code, удобные контрукции. Единственное, что всегда в нём раздражало, очень уж там неудобно сделана работа с большим количеством аттрибутов у тегов (что сейчас скорее правило, нежели исключение). На что мне приходилось писать свои костыли.


                1. apapacy
                  23.04.2018 08:36
                  +1

                  Ну это все вопрос вкуса о которых не спорят.по синтаксису я даже не задумывался что мне больше нравится. А вот тот момент что в реакте компонент является классом javascript позволяет получить не только эстетическое чувство но и реальные бонусы. Хотя как язык шаблонов реакт вызывает скорее отрицательные эмоции


                1. vintage
                  23.04.2018 09:58

                  Вам определённо должен понравиться view.tree, где:


                  1. отступы семантические, никаких закрывающих тэгов
                  2. аттрибуты всегда на отдельных строках, если их больше 1
                  3. никаких фигурных, квадратных, круглых скобок и даже кавычек
                  4. не нужно экранировать строковые литералы
                  5. никакой логики в шаблонах, логика только в скриптах
                  6. все вычисления ленивые
                  7. на выходе из любого шаблона получается плоский TS класс, от которого можно отнаследоваться опять же через view.tree
                  8. любой элемент доступен в рантайме через уникальное семантическое свойство класса
                  9. свойства внешнего и внутреннего компонентов лего связывать разносторонними связями


                  1. faiwer
                    23.04.2018 10:04

                    Вполне вероятно. Во всяком случае я неразделяю этой ненависти по отношению к Tree, которую выливают на вашу библиотеку. Но на вашем месте я бы сделал ещё какую-нибудь lite-версию, в которую не нужно въезжать, если вы хотите побольше сторонников.


                    Правда п.9 пугает. Это не антипаттерн?


                    1. vintage
                      23.04.2018 10:51

                      Требования не могут быть антипаттерном. Типичные требования: показывать актуальное состояние и контролы для его изменения. Другое дело, что, например, в ангуляре (что в первом, что в последнем) нативное двустороннее связывание реализовано отвратительно, поэтому его стараются реализовывать вручную, через события.


              1. faiwer
                23.04.2018 08:37

                На мой взгляд, многие проблемы JSX легко решаемы, если сделать какой-нибудь JSX 2.0. К примеру:


                <div class=`${prefix}_test`/>

                Т.е. убрать {??}, в пользу просто "``".


                <If {someBool}>
                  <tag1/>
                <Else>
                  <tag2/>
                </If>

                Вместо тернарников, вместо && и пр… Пусть это более громоздко, зато построено на отступах. А такой код в 99% случаев читается и понимается гораздо быстрее. И кровь из глаз не идёт.


                <For data of={someCollection}/>
                    <Item 
                        {...{ data }}
                        key={data.id}
                    />
                </For>

                Не только без .map, а вообще внедрить поддержку итерируемых коллекций из ES6. 21 век на дворе, надо бы! ;)


                <div>var1</div>
                <div>`text`</div>

                Инверсия. Переменные без {}, а текст с ними. Это уже тысячу раз предлагали. Причина простая — текст нужен редко (обычно i18n), а {} повсеместно.


                Ну и др… В этом случае мы избавимся от хаков, получим indent код, поддержку итераторов, отличную наглядность и читаемость.


              1. MadLord
                23.04.2018 08:43

                Т.к. перешел на JS с Perl и PHP, то никак не пойму — что не так с JSX? Что в PHP HTML внутри PHP, что в JSX HTML внутри JS.
                PHP

                <select>
                <? foreach ($array as $item ) { print "<option value='$item'>$item</option>" } ?>
                </select>
                

                JSX
                <select>
                {array.map((item) => (<option value={item}>{item}</option>))}
                </select>


                1. faiwer
                  23.04.2018 08:48

                  PHP шаблоны по мнению многих (я их ярый сторонник) это худшее, что придумало человечество, после Дом2. После них всё что угодно играет красивыми красками. Я думаю такое огромное засилие php-шаблонизаторов всех мастей (даже XSLT! даже в 2018г XSLT до сих пор используют как шаблоны для PHP) является хорошим ответом на ваш вопрос ;)


                  Лет 5 назад я писал реализацию Pug (тогда Jade) на PHP, ввиду отсутствия indent-шаблонизатора. На рынке было огромное засилие строковых вроде усов, смарти и пр… Мне кажется их там под сотню было. С тех пор за PHP не слежу, трендов не знаю.


                  1. MadLord
                    23.04.2018 08:57

                    Опять эти магические «многие» )))


                    1. faiwer
                      23.04.2018 09:08

                      Ну а что вы хотите. Где мы можем взять точные числа? В лучшем случае провести опрос на habrahabr-е или stackoverflow. Да и даже в этом случае как мы будем отсеивать мнения архитекторов, джунов, тимлидов, синиоров и пр.? Я сужу по опыту общения на хабре и др. IT ресурсах, по опыту работы меня и моих коллег в разных конторах. По долгосрочным проектам, в которых копится технический долг. В целом отношение к PHP-шаблонам крайне негативное. Это как клубок перепутанный, никакая подсветка синтаксиса не помогает. Теги в строках, интерполяция двух видов повсеместно. В худшем случае туда ещё и SQL пихают (но это к докторам надо).


                      Я могу конструктивно описать, что не так, по моему мнению, с PHP шаблонами. Но я думаю вы и так все мои аргументы знаете наперёд, просто оцениваете их иначе. Даже тот код выше я бы написал иначе (мне же потом его читать через пару лет, а может даже править, не одноразовый же код пишем).


                      <select>
                        <? foreach ($array as $item )
                            print "<option value='$item'>
                                $item
                            </option>"
                        ?>
                      </select>

                      Чем меньше нужно усилий на восприятие кода, тем лучше. Лаконичность не является универсальным решением. Лаконичность хороша, когда она лишает код мусора, но не когда мы всё пишем в одну строку, и, тем самым, значительно усложняем её сложность.


                      Скажем в eslint есть правило ограничивающее макс. сложность строки. Не спроста.


                      1. MadLord
                        23.04.2018 09:26

                        Да, с форматированием кода полностью согласен. Просто старался передать суть, а не красоту )))
                        А насчет «многих» — если нет адекватной статистики, то лучше говорить «в моих кругах».


                      1. vintage
                        23.04.2018 10:09

                        Не надо писать на php в стиле jsx.


                        <select>
                          <? foreach ($array as $item ): ?>
                              <option><?= $item; ?></option>
                          <? endforeach; ?>
                        </select>


                        1. faiwer
                          23.04.2018 10:10

                          Да, так лучше. Но я для себя вопрос решил иначе. Перестал писать на PHP :) В итоге постепенно всё забываю.


              1. drai3
                23.04.2018 11:38

                Когда кодовая база становится большой — начинаешь калёным железом выжигать всё похожее на это:

                Не вижу никакой проблемы в таком коде. Если ваша команда использует достаточно строгий airbnb eslint конфиг, ограничивающие такие строки до 100 символов длины, то всё читается прекрасно все зависимости от размера вашего приложения.


                В первую очередь из-за удобных отступов. Ну и финальный штрих это выжигание .map и && за счёт jsx-control-statement (на что не каждая команда согласится).

                Я вот в упор понять не могу чем вам ужасный For нравится больше чем красивый "функциональный" подход с map? А насчет if — && вы вполне можете использовать do-expressions из stage-1 без всяких там jsx-control-statement


                return (
                  <nav>
                    <Home />
                    {
                      do {
                        if (loggedIn) {
                          <LogoutButton />
                        } else {
                          <LoginButton />
                        }
                      }
                    }
                  </nav>
                )


                1. faiwer
                  23.04.2018 11:58

                  Чем? Тем что <For/> это XML. И он в XML-like коде. Рыба в воде. И indent-ы со всеми вытекающими. А "красивый" map в XML это тот самый рыбокит с грудой мусорных спец. символов. И не рыба, и не кит. Это полноценный полновесный JS-expression, там где его не ждёшь, а ждёшь просто удобного примитива для итерации, которые есть ну практически во всех шаблонизаторах (кроме JSX), но на которые почему то в этом топике катят бочку.


                  От красивой примитивной декларативной XML-схемы HTML, мы уходим слишком далеко. Естественно страдает читаемость. Зачастую это ещё и очень много-строчно как в вашем примере с Do. Либо месиво, если подсократить. Синтаксически такой код тяжелее для восприятия.


                  Ну вот сравните:


                  // 1-й
                  {
                    do {
                      if (loggedIn) {
                        <LogoutButton />
                      } else {
                        <LoginButton />
                      }
                    }
                  }
                  
                  // 2-й
                  <If condition={loggedIn}>
                      <LogoutButton/>
                  </If>
                  <Else>
                      <LoginButton/>
                  </If>
                  
                  // 3-й
                  <If {loggedIn}>
                      <LogoutButton/>
                  <Else>
                      <LoginButton/>
                  
                  // 4-й
                  :if loggedIn
                      <LogoutButton/>
                  :else
                      <LoginButton/>

                  3-й и 4-й максимально просты в восприятии, и при этом максимально коротки. В варианте 2 приходится закрыть </If>, но это всё ещё не является полноценным JS-выражением, и соответственно не требует усилий мозга на парсинг JS в шаблоне. Идеальный react-шаблон, в моём понимании, этот тот, где предельная сложность JS-выражения это скажем: !this.isLoggedIn().


                  По сути говоря, если хочется написать и забыть (одноразово), то что-нибудь типа <?= ?> из php это идеальный вариант. Вставляй куда угодно, пиши, всё что угодно. Максимальная свобода.


                  1. MadLord
                    23.04.2018 12:13

                    Пример не показателен. Вот он же на JSX — в чем существенная разница с 3 и 4 примерами?

                    {(loggedIn)
                        ? <LogoutButton/>
                        : <LoginButton/>
                    }


                    1. faiwer
                      23.04.2018 12:15

                      В этом:
                      image


                    1. faiwer
                      23.04.2018 12:20

                      {(loggedIn)
                          ? <LogoutButton
                              attr1={1}
                              attr2={2}
                          >
                              <b>{translate('Log In')}</b>
                          </LogoutButton>
                          : <LoginButton
                              attr1={3}
                              attr2={4}
                          >
                              <b>{translate('Log Out')}</b>
                          </LoginButton>
                      }

                      А это вообще месиво. Обратите внимание на то, что Открывающая часть тега и закрывающая находятся на разном удалении (:, ?). Обратите внимание на то, что всё похоже на свалку. И ведь тут ещё нет методов (теперь их везде любят пихать). С методами начнутся очередные () => ({ }) и новые нарушения indent-а. Повсюду нужно будет искать компромис "ещё не совсем уродливо, но уже не на половину экрана".


                      Помимо прочего финальная } связана чёрт знает с чем. Нужно много распарсить глазами. Реальный JS код. Всю его структуру. XML же в этом плане прост как доска. И потому не требует усилий.


                      1. MadLord
                        23.04.2018 12:26

                        Согласен насчет {}. Но остальное не проблема JSX, а проблема разработчика:

                        {(loggedIn)
                            ? <LogoutButton {...props1} />
                            : <LoginButton {...props2} />
                        }

                        Все остальное реализуется в отдельных SFC компонентах.


                        1. faiwer
                          23.04.2018 12:37
                          +1

                          Не понял вашего комментария. Вы предлагаете готовить props1 и props2 выше return? Готовить что-то в одном месте, а применять в другом это всегда компромисс. Просто потому, что если что-то лежит по месту, то видно сразу и не надо искать. Но не всегда это оптимально, т.к. может захламить. Вопрос конкретного куска кода. В случае ?: хватает 2 аттрибутов, да? :) И кстати такой фокус не прокатит в случае какого-нибудь .map(). Либо вы будете прямо в .map((item, idx) => { const props1 = писать. Чем дальше в лес, тем злее волки. Это про JSX. Всякий раз прибегая к хакам вроде ?:, &&, .map((item, idx) => и пр., мы неизбежно сталкиваемся с кучей проблем:


                          • ломаем indent
                          • либо не ломает но дико плодим строки
                          • груды круглых и фигурных скобочек
                          • необходимость парсить весь код глазами
                          • часть вещей готовить выше, или выносить в отдельные методы, просто чтобы хоть как-то понять что здесь творится
                          • плодить компоненты в пару строк, сильно увеличивая энтропию и вложенность древа, там где проблема исключительно в хаках. Ну и т.д..

                          И как бы мы не улучшали это действо, если бы в языке JSX были бы удобные примитивы, то мы бы вообще не встречались в этими проблемами. Просто по определению. И можно было бы подключить какой-нибудь linter к eslint, который бы считал синтаксическую сложность вложенных js-expression, и если бы она превышала скажем такую: !func(), то линтер бы бил по рукам. Сейчас это невозможно, потому что с таким линтером ни 1 средней сложности компонент нормально не написать.


                          ^ таких проблем не бывает в шаблонизаторах, где об этом подумали. Это именно JSX фишка.


                          1. PaulMaly Автор
                            23.04.2018 12:52

                            Прям транслируете мои мысли, когда я последний раз связывался с JSX.


                            1. faiwer
                              23.04.2018 13:00

                              Там ничего не изменилось с тех пор. Только теперь в React-е очень часто используется подход function-as-child. Например посмотреть на новые <Consumer/>. Многие UI-либы используют его (всякие там анимации, sort-ы и пр.).


                              1. PaulMaly Автор
                                23.04.2018 13:11

                                Из последних фишек, которые я слышал были Context и render props. После беглого изучения которых, мне показалось, что React пытается как-то еще сильнее усложнить свой flow неочевидными пробросами и т.п.


                    1. faiwer
                      23.04.2018 12:24

                      В запущенных случаях оно начинает напоминать это: