Pull to refresh

Comments 97

UFO just landed and posted this here
Не сказал бы что Svelte многословнее в шаблонах, чем тот же Vue, Ractive, ну и Angular, да. Опять же смотря как писать. Просто я чаще всего использую своеобразный «view driven development», т.е. иду от потребителя (вьюха). Поэтому иногда у меня императивщины больше в шаблонах, чем в скриптах, которые выходят весьма декларативными.
Да, открыл статью, увидел синтаксис, закрыл статью.
Вы поступили крайне непрофессионально. Не стоит об этом хвастаться в приличном обществе.
Хорошо хоть не забыли в комментарии отписаться ;-) Судя по комментариям ниже, хорошим синтаксисом вы считаете JSX? Ну чтож, это ваше право. Думаю найдется как много сторонников, там и много противников этого синтаксиса.
Нет, у каждого синтаксиса есть свои слабые стороны. Речь велась о конкретном
Речь? Мне показалось это не более чем ваше мнение. Лично для меня псевдо-js в js значительно менее очевидная вещь, но я и не отношу себя к любителям реакт.

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


{#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>)}

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


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

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

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


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

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

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

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=>)) не сколько в каком-то в том визуальном удобстве или даже не в отсутствии необходимости учить кастомный синтаксис а в банальной возможности протипизировать шаблон

Оно компилируется в 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. И что?

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

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

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 и мы получаем возможность полного тайпчекинга шаблонов

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

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

Первый вопрос: все-таки почему же это не «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 и там и там сильно похожи, но по-сути это разные вещи.

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


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


<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

Если сравнить с таким же 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, он дополняет, а не ломает.

Спасибо за столь терпеливый и объемный ответ. Я обязательно перечитаю всё ещё раз и попробую глубже понять суть, но сразу хочу заметить, что ваш пример сравнения 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 лаконичнее.

ваш пример сравнения 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 */  
   });


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

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

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

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

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

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

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

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

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

REPL

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


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

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

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

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

Про то как точно реализованы биндинги я не знаю и пока они работают мне не интересно)))
Но как он реализован?
Я немножко продебажил, работает примерно так:

1) Обновляем стейт .set(new)
2) Происходит сравнение старого и нового стейта, выявляются измененные ключи, обновляется стейт, если изменения есть, идем дальше
3) Вызывается set для всех связанных компонентов. В данном примере Stepper.set -> App.set -> Stepper.set, происходит рекурсивный вызов set, на последнем шаге рекурсия останавливается т.к. изменений нет (не проверял есть ли защита от бесконечной рекурсии, в коде этого не заметил)
4) Рендеринг, каждый биндинг проверяет изменились ли его переменные, если да — вычисляем выражение и выводим результат

Кроме того, в процессе рассылаются события, вызываются пустышки (для возможности встраивать хуки)

Это подход из серии observable (knockout.js и подобные), отсюда computed и пр. болячки.

В общем интересно «потыкать» фреймворк-less реализацю, но на практике, думаю, оно не особо полезно, особенно для толстых + гибких приложений, скорости оно не добавляет, плюс — что не нужно тащить фреймворк на клиент, но зато «перекомпилленые» компоненты тяжеле их исходника — т.е. будет проигрывать по весу в больших приложениях, и нет гибкости типа генерации компонентов на лету и т.п. (хотя оно не часто нужно). Ещё, судя по коду, скорость формрования DOM медленнее чем ангуляровский подход.
А, ну в точности, как в Ангуляре с соответствующими болячками:
1. невозможно понять пришло ли изменение снизу (от контрола) или сверху (от модели).
2. излишний оптимизм — мы уже изменили и полагаемся на это изменение, а сервер может и не принять.
3. неконсистентное состояние приложения пока не закончится синхронизация.
4. это банально медленно (из-за кучи проверок) и потребляет память (состояние много раз дублируется)
в точности, как в Ангуляре
В ангуляре идет отслеживание всех конечных выражений (биндингов) — dirty-checking. В данном случа идет проверка изменения стейта. Это ключевое отличие фреймворка.

1. невозможно понять пришло ли изменение снизу
Неважно от куда пришло, главное что бы событие источник не получил свое событие назад. Там где важно — используются односторонние биндинги (хотя может это не совсем про ангуляр).

2. излишний оптимизм
Это заваист от приложения (ТЗ), как оптимистично оно должно быть.

3. Спорный вопрос, +- везде так.

4. В данном фреймворке этот момент сделан максимально оптимально, даже на vanilla-js оптимальней не будет*, наример биндинг {{foo(x + 5)}} компиллируется примерно в
if(changed.foo || changed.x) {
  targetElement.nodeValue = state.foo(state.x + 5);
}

То есть, если в стейте есть, например, функция, которая полагается на внешний стейт, то компонент не отдетектит изменение этого внешнего стейта? Ну это аналог ангуляровского OnPush с той лишь разницей, что в ангуляре чекаться будут лишь Input-ы.

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

Оптимизм всяко не должен быть по умолчанию в основе фреймворка. Это богатый источник хитрых багов.

Есть фреймворки, где нет синхронизации, а работа происходит с состоянием родителя ($mol, условно redux). Есть, где временная рассинхронизация не видна внешнему наблюдателю ($mol_atom, MobX).

если в стейте есть, например, функция, которая полагается на внешний стейт, то компонент не отдетектит изменение этого внешнего стейта?
Даже больше, если функция зависит от внутреннего стейта — она не будет задетектина.
Для отлова (внутренних) видимо используются compound.

Даже не функция, а просто переменная по ссылке — не будет отслежена. «state.one = state.two» — при изменении state.one.name, изменения state.two.name не будут обнаружены. Детектор максимально простой в три строки (вот они), поэтому под него нужно подстраиваться.

Да, это не совсем observable, а что-то совсем примитивное, но т.к. оно распространяется по всем прибинденым компонентам — это похоже на observable, но не на уровне переменных, а на уровне компонентов.

Довольно странно в ответ на обновления от сервера тут же слать ему эти же обновления.
Это учтено:
Неважно от куда пришло, главное что бы событие источник не получил свое событие назад
Уверен, что в $mol все сделано значительно лучше)))

невозможно понять пришло ли изменение снизу (от контрола) или сверху (от модели).

Обычно это и не нужно. Если нужно это четко понимать, то лучше вручную ловить ивенты от контрола.
излишний оптимизм — мы уже изменили и полагаемся на это изменение, а сервер может и не принять.

Конкретный пример можно? Я например использую чаще всего «optimistic UI», который не плохо работает с таким подходом.
неконсистентное состояние приложения пока не закончится синхронизация.

Пока не было с этим проблем, видимо синхронизация работает хорошо.
это банально медленно (из-за кучи проверок) и потребляет память (состояние много раз дублируется)

Но ведь бенчмарки показывают обратное. И память и скорость на выстоте.

Вообще, насколько я понял из вашего доклада по ОРП, который вы читали на Frontendconf, то в $mol обсчет вообще идет в двух направлениях — туда и обратно. Может я не понял конечно, но выглядит как двойная работа.
Вы зря смеётесь, в $mol этот момент действительно сделан лучше. Там нет никакой двойной работы. В случае биндингов там вообще нет никакой работы — просто вложенный компонент работает напрямую с состоянием владельца.

Я бы рекомендовал больше думать про unhappy path — тут излишний оптимизм больно бьёт по голове.

Ну, покажите какую-нибудь сложную форму с хитрой валидацией или виртуальный скроллинг хотя бы, посмотрим как там всё хорошо с синхронизацией)

Бенчмарки — они все на простых примерах, где максимум 3 уровня вложенности состояний. Но я согласен, инлайнинг существенно снижает накладные расходы.
Но я согласен, инлайнинг существенно снижает накладные расходы.
Инлайнинг делают многие фреймворки из топа, так что это не «ноухау».
Вы зря смеётесь, в $mol этот момент действительно сделан лучше.

Да я не смеюсь. Благодаря вам все на хабре (и не только) знают что $mol идеален и дальше живут с этим знанием.

Я бы рекомендовал больше думать про unhappy path — тут излишний оптимизм больно бьёт по голове.

Может я не правильно понял ваш кейс, но я обычно делаю как-то так (аля optimistic ui):

const { article } = this.get();
api.post(`articles/${article.slug}/favorite`)
   .then(article => this.set({ article }));

// optimistic UI
if (article.favorited) {
  article.favoritesCount -= 1;
} else {
  article.favoritesCount += 1;
}
article.favorited = !article.favorited;

this.set({ article });


виртуальный скроллинг хотя бы, посмотрим как там всё хорошо с синхронизацией)

Вот небольшой пример: REPL
Как я и говорил, unhappy path вообще не обрабатывается. Что произойдёт, например, в оффлайне?

В примере с виртуальным скроллом типичные болячки:
1. пол гига видео памяти в трубу
2. полный рефлоу+репейнт каждый раз когда меняется состав отображаемых элементов
3. синхронная обработка скролла, что приводит к тормозам скроллинга из-за тормозов рендеринга

Как я и говорил, unhappy path вообще не обрабатывается. Что произойдёт, например, в оффлайне?

А с чего вы взяли, что обработка подобных кейсов не реализована в рамках сервиса api? Вообще зачем вьюхе следить доступен ли бекенд?

В примере с виртуальным скроллом типичные болячки:

Это просто пример и если честно я не замерял никак. Работает быстро и этого достаточно. Никаких тормозов скролла я не вижу, все четко и плавно. И очень сомневаюсь в ваших цифрах, особенно в пол гига видео.

Чтобы не обманывать пользователя.


Там демка размером с замочную скважину с тривиальным контентом. Ещё бы оно тормозило.



Идентификаторы, кстати, чудесные. Сразу понятно, что есть что.

Честно говоря не силен в профайлинге, поэтому без понятия что за скриншот вы предоставили. У себя померил память 2-мя способами, больше 26-27Мб не видел.

Там демка размером с замочную скважину с тривиальным контентом. Ещё бы оно тормозило.

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

Идентификаторы, кстати, чудесные. Сразу понятно, что есть что.

Это как бы component-scoped стили, которые генерируются автоматически. Вы их как предлагаете сделать более человекочитаемыми?
Кстати про «component-scoped стили», есть несколько путей реализации, а для браузеров линейки Chrome (~70% покрытие) оно работает нативно.
Спасибо за ваше исследование. Пара ремарок:
2) Происходит сравнение старого и нового стейта, выявляются измененные ключи, обновляется стейт, если изменения есть, идем дальше

Полагаю в этом фактически нет необходимости, так как метод set(), как правило, принимает лишь обновленную часть стейта, а не весь стейт, т.е. мы уже знаем какие ключи изменились.
Это подход из серии observable (knockout.js и подобные), отсюда computed и пр. болячки.

Скорее уж подход похож на RactiveJS, так как один автор, а он всегда неплохо справлялся с точеными изменениями, без «dirty-checking».
но на практике, думаю, оно не особо полезно, особенно для толстых + гибких приложений, скорости оно не добавляет

Подход новый и не проверенный, это правда, но почему вы решили, что он не будет полезен? Особенно в контексте, например, смежного подхода «micro frontends»? Могли бы вы как-то шире раскрыть свою мысль?
но зато «перекомпилленые» компоненты тяжеле их исходника — т.е. будет проигрывать по весу в больших приложениях,

Это не так. Этот момент уже обсуждался в комментариях к моей первой стате про Svelte. Если кратко, (1) Svelte влючает в бандл только тот код, который используется. Если вы, например, не используете computed-свойства, то код из реализации не будет присуствовать в бандле; (2) Svelte умеет выделять общий код для всех компонентов и включать его единожды в бандл, поэтому накладные расходы на каждый компонент минимальны — создание конструктора; (3) — Рич Харрис, фактически, написал одну из лучших реализаций tree-shaking'а (Rollup) и он хорошо понимает как оный работает, именно поэтому Svelte в основе имеет походы, которые хорошо сочитаются с ним.

Да в действительно большом приложении, может наступить такая точка, в котором размер бандла Svelte и размер бандла фреймворк + код приложения, сравняются, но во-первых, я бы наверное на стал писать настолько большие приложения на Svelte, также в общем-то как и на React. Во-вторых, с таким кол-вом кода пора делать code-splitting и даже с ним Svelte очевидно будет работать лучше.
Ещё, судя по коду, скорость формрования DOM медленнее чем ангуляровский подход.

Насколько мне известно, Angular 5 использует некоторые оптимизации для того, которые пагубно сказываются на памяти, что собственно видно из бенчмарков.

Показатель «slowdown geometric mean» в non-keyed results: Angular 5 1.19 / Svelte 1.21 (отрыв минимален), но при этом если посмотрите на потребление памяти, то там Angular «краснеет» от стыда, в то время как Svelte все еще бодр с зелен.

В keyed results все вообще 1.29 к 1.47 в пользу Svelte, а Angular все еще жрет память.
лишь обновленную часть стейта, а не весь стейт
Все верно, проверка идет, на то что присваиваем.

Скорее уж подход похож на RactiveJS
Да кстати, к ниму ближе, хотя все равно это из серии «observable».

«перекомпилленые» компоненты тяжеле их исходника
Я увидел что шаблоны (html) превращаются в очень развесистый js, и если оно точно так же работает для больших компонент, то результат должен не слабо перевешивать исходник, но это надо проверять. + все компоненты содержат куски «фреймворка» в себе.

скорость формрования DOM медленнее чем ангуляровский подход
Тут я имел ввиду ангуляр 1, где шаблон просто вставляется в DOM, и это быстрее чем собрать его по элементам, особоенно где много html и мало биндингов. Впринципе это не особо важный момент.

Подход новый и не проверенный, это правда, но почему вы решили, что он не будет полезен?
В кратце, единственное преимущество этого фреймворка — его отсутсвтие на клиенте, т.е. меньше вес бандла, все остальные фичи есть в других фреймворках.
На сколько важно +-50кб к бандлу? Для большого приложения это не важно, на первом плание совсем другие фичи.

Да, это выглядит круто и интересно, но при выборе фреймворка это будет играть последнюю роль, + его отсутствие на клиенте несколько снижает гибкость (нельзя поюзать как jquery).

А вот как компиллятор для самостоятельных библиотек/компонентов может быть интересен, там вес важен, хотя тоже спорно — «кто-то смотрит что либа тянет jquery? ведь jquery итак уже в проекте.»
Да кстати, к ниму ближе, хотя все равно это из серии «observable».

Согласен, да.

Я увидел что шаблоны (html) превращаются в очень развесистый js, и если оно точно так же работает для больших компонент, то результат должен не слабо перевешивать исходник, но это надо проверять. + все компоненты содержат куски «фреймворка» в себе.

Вы наверное смотрели REPL. Дело в том, что REPL компилируется не оптимальным образом именно для того, чтобы вы могли посмотреть код каждого компонента в полном объеме. Если же собирать бандл из нескольких компонентов с помощью webpack-loader или rollup-plugin, то там по умолчанию включены опции оптимизации, в том числе shared: true, которая как раз препятствует повторному включению кода в бандл. Поэтому нет, не все компоненты содержат куски «фреймворка» в себе.

Более того, я специально спрашивал Рича, какой оверкост будет на каждый компонент и вот его буквальный ответ:

function SvelteComponent(options) {
    init(this, options);
    this._state = assign({}, options.data);
    this._fragment = create_main_fragment(this._state, this);
    if (options.target) {
        this._fragment.c();
        this._fragment.m(options.target, options.anchor || null);
    }
}
assign(SvelteComponent.prototype, proto);
export default SvelteComponent;

И у него есть планы оптимизации и этого куска. Все это уже обсуждалось в комментариях к статье Магически исчезающий JS фреймворк.

Тут я имел ввиду ангуляр 1, где шаблон просто вставляется в DOM, и это быстрее чем собрать его по элементам, особоенно где много html и мало биндингов. Впринципе это не особо важный момент.

Честно говоря Angular 1 вообще по бенчмаркам в аутсайдерах. Логику вашу я наверное понимаю, но факты говорят об обратном, он очень медленный по сравнению со Svelte.

В кратце, единственное преимущество этого фреймворка — его отсутсвтие на клиенте, т.е. меньше вес бандла, все остальные фичи есть в других фреймворках.
На сколько важно +-50кб к бандлу? Для большого приложения это не важно, на первом плание совсем другие фичи.

Статистику по размеры бандла я приводил в комментах выше. По моему опыту когда я заканчиваю писать приложение на Svelte, размер всего приложения меньше, чем весь только лишь подключенного Vue (не говоря уже о React/Angular/Ractive), без написания даже строчки кода приложения. Это очень приятно, уверяю вас.

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

Это насчет веса. По поводу «единственное преимущество», это тоже не так. Он дает преимущества по всем основным показателям: скорость работы, скорость старта, память и вес бандла, конечно. Вы можете изучить соответствующие бенчмарки, по некоторым из них он даже лучше чем vanilla js реализация.

Далее, мне нравится формулировать так, что Svelte — это способ писать vanilla js приложения (потому важен «performance»), без необходимости писать на vanilla js (потому что важно избежать «complexity»). Вот как сам Рич формулирует это:

You can't write serious applications in vanilla JavaScript without hitting a complexity wall. But a compiler can do it for you.


Согласен с этим лишь частично:
Да, это выглядит круто и интересно, но при выборе фреймворка это будет играть последнюю роль, + его отсутствие на клиенте несколько снижает гибкость (нельзя поюзать как jquery).

Если вы говорите о том, что для Svelte необходим обязательный шаг сборки, то да, это так. Однако тоже самое справедливо для тех же Angular (Typescript) и React (JSX) — все это требует шага сборки. Однако, после этого шага, компоненты Svelte не просто могут использоваться как обычный JS и подключаться через обыкновенный <script /> и их можно поюзать как jquery, но и компонент написанный на Svelte можно влегкую интегрировать в приложение на любом другом фреймворке. У меня был такой опыт по замене нескольких «медленных» Ractive компонентов, на более быстрые аналоги на Svelte путем написания Ractive-декоратора для них. Очень удобно, а jquery — это уже, имхо, моветон.
Он дает преимущества по всем основным показателям: скорость работы, скорость старта, память и вес бандла, конечно.
Если это ваши ключевые показатели, то там десятка других фреймворков перед svelte.

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

Для меня на первых местах стоит фичи и гибкость.

я имел ввиду ангуляр 1, где шаблон просто вставляется в DOM
Ангуляр тормозной по другим причинам, я про вставку (вообщем зря я про это начал), в «вашем» тесте можете посмотреть скорость вставки для angular-light: 1.6сек потив 2.2сек у svelte, т.е. вставка блоком быстрее чем по элементам.

jquery — это уже, имхо, моветон.
на jquery до сих пор подавляющее кол-во библиотек написано (да и весь* «интернет»).

Вообщем, ваша позиция понятна, фреймворк как минимум интересен, будем наблюдать ;)
Если это ваши ключевые показатели, то там десятка других фреймворков перед svelte.

Дико извиняюсь, если дал повод думать, что «топлю» за то, что Svelte лучше всех и во всем. Эту должность давно и надолго занял $mol. ;-)

Я скорее имел ввиду, что он имеет хорошее сочетание всевозможных показателей, в том числе фичи и гибкость. Например, есть inferno, который немного быстрее по скорости, но при этом чуть проигрывает в запуске и памяти, а сильно проигрывает по фичам. Иными словами Svelte именно хорошо сбалансирован.

Особенно это заметно, если сравнивать его не с маргинальными либами, типа surplus, bobril и т.п. о которых никто и не слышал, и которых полно в бенчмарках. А с «большой тройкой», а мне бы хотелось поставить Svelte именно в один ряд с ними. В этом сравнении сразу видно, что Svelte и быстрый и не прожорливый, и худенькие бандлы имеет, и при этом достаточно «feature-rich» и использует новые интересные подходы.

Ангуляр тормозной по другим причинам, я про вставку (вообщем зря я про это начал), в «вашем» тесте можете посмотреть скорость вставки для angular-light: 1.6сек потив 2.2сек у svelte, т.е. вставка блоком быстрее чем по элементам.


Это все же не мои бенчмарки, а более-менее общепринятые во фронтенде. Сам я также считаю, что по большей части они бесполезные, просто других данных у нас нет, если требуется сравнить. Только бенчмарки и демо-приложения, типа ToDoMVC или RealWorld и получается, что Svelte и там и там хорош. Остальное за разработчиком, вы правы.

Относительно alight, я уже написал, что Svelte не пытается быть абсолютно лучшим по отдельно всем показателям, а скорее быть хорошо сбалансированным. Если брать и выдергивать конкретные показатели, тогда можно выделнуть, например, swap rows (18.1 / 117.0) или remove row (48.2 / 133.5), оба в пользу Svelte с отрывом намного большим чем, create rows в пользу alight. И кстати, на эту тему уже есть issue, по использованию innerHTML втавок там где это возможно.

на jquery до сих пор подавляющее кол-во библиотек написано (да и весь* «интернет»).

Да, согласен. Поэтому хорошо, что в Svelte теперь есть actions, которые очень удобно позволяют интегрироваться, в том числе с jQuery плагинами.

например, swap rows (18.1 / 117.0)
Обратите внимание, этот тест (строка) делится на 2 показателя для всех вреймворков = ~20 либо ~120, это просто показывает у кого есть заточка этого конкретного кейса, а у кого нет (впринципе несложно эту заточку добавить).
Опять же, я не фанат разбора каждого показателя в отдельности. Я считаю что наилучшая оценка по «принципу светофора» — смотрите на колонку фреймворка в целом по всем показателям и видите, что со Svelte у вас исключительно «зеленый» — можно ехать!
image
Быстрый фреймворк не спасет криворукого, а вот правильно написанное приложение будет быстро работать на любом нормальном фреймворке, т.к. они все достаточно быстры.

У вас неверная аксиоматика. Некриворуких программистов не бывает. Все мы люди и у всех свой радиус кривизны рук. И правильная архитектура должна снижать влияние кривизны рук программиста на качество результата. Сравните два тезиса:


  1. На React очень легко сделать нерасширяемый компонент.
  2. На $mol очень сложно сделать нерасширяемый компонент.

В каком случае компонент с большей долей вероятности получится расширяемым у обычного не божественного программиста со стоящим над душой менеджером с дедлайном в руках?

Для интереса взвесил ваш todo пример:
Исходник: ~180 строк
Результирующий js: ~1450 строк, ~41kb
Минифицированный js: 22kb
js + gzip: 7.9kb
Не знаю, как вы это взвешивали. Надеюсь не прямо из REPL?

Мои результаты такие:
bundle.js: 13.7kb
bundle.js + gzip: 5.4kb (т.е. то что улетает в браузер)



На самом деле, это даже много. Просто так как смысл демо быть показать максимум фич, я туда напихал все что влезло))))
Документация не ахти, вопрос: как из директивы (enterKey) получить доступ к стейту — к текущему todo, если она внутри цикла, например при нажатии enter на input.inline-input добавить текст в todo.body, вызвать set из директивы enterKey?
enterKey — это не директива, это custom event. Директива — это on:

Внутри директивы on: можно вызывать методы и передавать в них все что вам нужно:

{#each items as item}
  <button on:click="myMethod(item)">Click on item</button>
{/each}

В случае с моей демкой, текст уже редактируется, потому что там 2way биндинг на todo.body.
Это понятно, я имел ввиду что-то типа этого — навешать на элемент диретиву в которой будет доступ к части стейта, или ко всему стейту.
В svelte можно сделать кастомную директиву, или где найти пример интеграции с jquery?
В Svelte нельзя делать кастомные директивы. Есть только ограниченный список директив и это осознанный рестрикшн, который специально уводит Svelte от angular-like подходов.

Далее читайте, пожалуйста, раздел этой статьи Actions, я как раз писал об этом. Условный пример с условным datapicker на jquery: REPL

Теперь понятно, я почему спрашивал — в svelte организация циклов и выражений с локальными переменными создали бы проблеммы при использовании их в директивах, а тут диретив просто нет.

Нет директив — нет проблем. Хотя опять же гибкость снижается, но по большей части, компонентов и action должно хватать. В ангуляре директивы были популярны потому что не было компонентов и сами директивы были не гибкие, сейчас многое поменялось.
Подоплеку я понял))) Вообще работа с объектами и массивами в Svelte реализована весьма примитивно и это не всегда удобно.

Когда я спрашивал у Рича почему так, он ответил, что сделано это специально, чтобы не усложнять отслеживание изменений и советовал придерживаться максимально плоских данных компонента. А решать подобные кейсы предложил композицией компонентов, что немного напоминает React.

Для меня сперва это было не очень привычно, потому что в том же Ractive вообще можно сумашедшие вещи делать, типа:

this.set('foo.bar.baz', 'hello world');
this.set('items.*.checked', true);

А все блочные выражения создают новый Context, с помощью которого вообще можно все делать. Думаю примерно понятно о чем я.

В Svelte такие вещи сделать не так просто, хотя можно конечно подключить setDeep из svelte-extras, но в целом чтобы удобно работать с локальными переменными в циклах, предлагается выделить их в отдельный компонент и там уже работать «плоско»:

{#each items as item}
    <Item bind:item />
{/each}
но, как минимум, код 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.

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

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

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


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

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


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

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

UFO just landed and posted this here

Ну да, шаблоны изначально применялись по назначению — генерация хтмл. А сейчас они выродились в композицию компонент, но зачем-то продолжают мимикрировать под хтмл, который уже совершенно не нужен. Я думаю через год-два придёт очередной фейсбук и поднимет хайп на тему ненужности хтмл. Уже сейчас можно наблюдать бурление на тему ненужности сырых строк в шаблонах — многие хотят, чтобы js значения были по умолчанию и строковые литералы лишь как частный случай. В Kotlin появилось инстанцирование классов без new, что даёт довольно лаконичный DSL для композиции компонент без каких-либо шаблонизаторов.

Не знаю, что там за «бурление», может у кого-то просто метеоризм, но не вижу в композиции компонент никакой особой мимикрии:

<video src="..." controls></video>
<todolist items="..."></todolist>


Выглядит довольно органично, имхо. А в империческом контексте нет особой разницы между стандартным тегом <video/> и кастомным — инкапсуляция сложного функционала в тег.
HTML — язык разметки текста. Именно поэтому он имеет такие выразительные средства как:
1. открывающий и закрывающий тег с произвольным текстом между ними
2. словарь аттрибутов привязанный к тегу

Для компонентной композиции всё это не нужно, зато нужны такие вещи как:
1. связывание
2. подстановки
3. переиспользование
4. различные типы данных
5. множественные поддеревья

Прикручивание всего этого дела к html иначе как костылями назвать сложно.
Для компонентной композиции всё это не нужно, зато нужны такие вещи как:

Это вы придумали, что все это нужно или что? Мне вот достаточно иметь возможность инкапсулировать какой-то комплексный функционал в рамки одного html тега и переиспользовать этот тег в разных частях приложения, ровно также как это делает тег video из примера выше.

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

Собственно, именно такой подход и используется в тех же Web Components, что уже стандарт. И практически всех компонентных фреймворках, которые так или иначе эмулируют его. От себя добавлю, что пока то что я делаю в итоговой своей форме имеет html, я бы хотел работать максимально близко к нему, а не подниматься на новые уровни абстракции с «нособрасами» и «дикорогами», ну или view.tree.

DOM !== HTML. Они даже близко не рядом.


Ну ка расскажите, как мне в тэг video на панель с кнопками добавить свой контрол или изменить существующий.

А это проблема HTML или конкретного api конкретного тега? Мы можем свои компоненты делать более кастомизируемыми, да и на video тег довольно легко навесить другие контролы. Вот у Svelte есть демка в кастомными контролами, правда для audio: REPL
UFO just landed and posted this here
Вы вот про все это написали, а у меня ничего не йокнуло. Не понимаю зачем мне в разметке все это.

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

Т.к. перешел на 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>

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


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

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

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


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


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

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


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

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

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


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

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

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

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

Чем? Тем что <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 это идеальный вариант. Вставляй куда угодно, пиши, всё что угодно. Максимальная свобода.

Пример не показателен. Вот он же на JSX — в чем существенная разница с 3 и 4 примерами?
{(loggedIn)
    ? <LogoutButton/>
    : <LoginButton/>
}
{(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 же в этом плане прост как доска. И потому не требует усилий.

Согласен насчет {}. Но остальное не проблема JSX, а проблема разработчика:
{(loggedIn)
    ? <LogoutButton {...props1} />
    : <LoginButton {...props2} />
}

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

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


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

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


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

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

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

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

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

Sign up to leave a comment.

Articles