Pull to refresh

Comments 288

Слишком круто звучит, кто-то использовал уже в бою? Какие подводные камни?
Маленькая экосистема пакетов. А для новой версии тем более – еще нужно время пока все основные библиотеки обновятся, пока еще даже не все пакеты в официальной организации github.com/sveltejs переехали

В общем, типичная ситуация курицы и яйца: мало пакетов потому что мало пользователей, а мало пользователей потому что мало пакетов.
Мы используем в бою начиная с бородатой 1.x.x. Полет нормальный! Причем настолько, что сперва Svelte полностью вытеснил RactiveJS, который мы использовали аж с 2013 года. А теперь по-тихоньку вытесняет Vue — скорее всего все новые проекты будем делать на Svelte 3 вместо него. Сейчас для 2-х проектов используем и Sapper, с ним пока проблем больше, потому что Sapper 3 еще в альфе, но думаю все наладится в ближайшее время.

p/s Под мы я имею ввиду:

UFO just landed and posted this here
Сложно сказать, потому что надо знать насколько код проекта завязан на экосистему реакта. Могу сказать только, что, в целом, Svelte значительно мощнее из коробки, а аналог React компонента на Svelte будет разработать проще и раза в 2 меньше кода.
Вас в презентации указали.
Какие подводные камни?

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

Кстати, я бы послушал о чем конкретно может идти речь. Можете какие-то конкретные требования указать?

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

Я же про это и говорю :)
Если вы используете все фичи, то они все в бандл и попадут, и тогда де-факто нет разницы с тем, что просто бы загрузили одну js-ку со всем функционалом :)
Аналогичная ситуация со всякого рода три-шейкингом и т.п. вещами — очевидный профит есть только тогда, когда вам из большой библиотеки нужен очень ограниченный функционал.


Кстати, я бы послушал о чем конкретно может идти речь. Можете какие-то конкретные требования указать?

Ну svelte — это чисто подсистема рендеринга, со всеми вытекающими. Даже банально санитайзера не завезли:


Svelte does not sanitize expressions before injecting HTML. If the data comes from an untrusted source, you must sanitize it, or you are exposing your users to an XSS vulnerability.

Понятно, что это как бы не баг, а фича, но разные проекты требуют разных фич.

Если вы используете все фичи, то они все в бандл и попадут, и тогда де-факто нет разницы с тем, что просто бы загрузили одну js-ку со всем функционалом :)

И все же разница есть. Во-первых, все фичи сразу использовать не получится, а значит есть возможность расти прогрессивно. Во-вторых, подход Svelte позволяет сделать code-splitting реально просто и без усилий, а значит по факту для работы каждой конкретной страницы нужно будет загрузить значительно меньше кода. Хотя в целом я вашу мысль понял, но примеры все же подтверждают значительную экономию по размеру результирующего кода.

Ну svelte — это чисто подсистема рендеринга, со всеми вытекающими. Даже банально санитайзера не завезли:

Это касается только непосредственного вывода строки html виде выражение. Учитывая что на npm +100500 подобных пакетов, не имеет смысл включать их в Svelte, тем более что никаких дополнительных действий для их использования не требуется:

<div>{@html sanitize(html)}</div>

<script>
  import sanitize from 'sanitize-html';

  let html = `<strong>hello world</strong>`;
</script>
Это касается только непосредственного вывода строки html виде выражение.

С санитайзером это просто достаточно очевидный пример. Речь о том что свелте сам по себе кроме рендера ничего, фактически, не содержит.


И все же разница есть. Во-первых, все фичи сразу использовать не получится, а значит есть возможность расти прогрессивно.

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

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

По сути да, это так. Все же это «UI framework». Хотя конечно есть еще встроенное управление стейтом, но тоже преимущественно для UI. С другой стороны, UI — это львиная доля фронденда. Такие вещи как роутинг или общение с сервером можно легко организовать поверх Svelte с помощью парочки stand-alone библиотек, как pagejs или axios.

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

Тут вообще сложно с чем-то не согласиться. Можно только добавить, что Svelte совершенно точно подходит для всего того, для чего подходят React или Vue. Angular все же играет немного в другой лиге.

Можно только добавить, что Svelte совершенно точно подходит для всего того, для чего подходят React или Vue. Angular все же играет немного в другой лиге.

Ну да, если сравнивать с react и vue то разница, конечно, сильно меньше, чем при сравнении с ангуляром, backbone и другими "толстыми" фреймворками.

Разве не будет ситуаций когда эти $$invalidate ставятся там где совсем не надо? Если уж есть свой компилятор, то почему бы не ввести новое ключевое слово? Например, я когда-то делал плагин для babel который заменял такой код:


cell firstName = 'Matroskin';
cell lastName = 'Cat';

cell fullName = firstName + ' ' + lastName;

console.log(fullName);

на такой:


const cellx = require('cellx');
const firstName = new cellx.Cell('Matroskin');
const lastName = new cellx.Cell('Cat');

let fullName = new cellx.Cell(() => firstName.get() + ' ' + lastName.get());

console.log(fullName.get());

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

При создании Svelte была задача иметь полностью валидный синтаксис Javascript, поэтому нет никаких чужеродных вкраплений. Да, тут есть моменты, когда привычные конструкции языка работают весьма неожиданно, но при этом, на мой вкус, очень логично.
Полагаю, что $$invalidate проставляется только для переменных, которые задействованы в шаблонах, либо в реактивных объявлениях(aka вычесляемые свойства), а это не видится мне сложной задачей.

только для переменных, которые задействованы в шаблонах

да, тоже подумал об этом после отправки сообщения. Тогда всё норм, единственно что переменная может использоваться в шаблоне, но быть внутри условия, которое сейчас неактивно. Тогда $$invalidate сработает, но вряд ли там что-то страшное (изменение DOM) произойдёт.

Ответ на главный вопрос жизни, вселенной и всего такого — undefined

Как красиво! Можно даже сказать, философски

Конечно, это же пошаговый учебник. Если будете не только смотреть код, но и читать описание и кнопочки всякие там нажимать, то пойдете почему так.
Не до конца осознал кейс, когда $$invalidate может проставляться там где не надо, вот мы пишем изменение какой-то переменной:

count += 1;


Раз к ней было выполнено присвоение, нужно пересчитать DOM и реактивные переменные, связанный с ней. Поэтому дописываем сюда $$invalidate. Более того, внутри как бы еще проверяется, изменилась ли переменная по-настоящему и если нет, то ничего не происходит, поэтому, по идее, сам по себе вызов $$invalidate очень дешевый — мы точно знаем что нужно чекать и в какой момент времени.
Выше уже разобрались, что $$invalidate ставится только для переменных используемых в шаблоне и реактивных объявлениях. Всё норм)

А как это вырубать? Например, я тысячу раз меняю count при нажатии на кнопку, но рендер мне нужен только в конце, когда состояние перестало меняться.

Меняйте стейт не связанный с шаблоном, а потом в нужный момент выставляйте связанный. Вообще не люблю синтетические кейсы. Одно дело когда для каких-то бенчмарков придумывают, а другое дело если в диалоге.
Удивительная выдержка у Рича — 37 минут говорит о том, что люди должны понимать уже через 5 минут после знакомства с Реактом. Желаю Svelte остановить это костыльное безумие.
Либо меня не поняли, либо как раз поняли и обиделись. Я говорил о том, что перевычислять дерево каждый раз и искать изменения — это костыльнейшее решение, которое можно было придумать и написать на коленке как Proof of Concept лет 10 назад. Если я не ошибаюсь, уже в AngularJS была настоящая декларативность. Почему же большинство выбирало изначально костыльное решение, строит поверх ещё ворох костылей и упорно защищает это уродство? Все babel-плагины: JSX, react-css-modules, hooks.macro — это же всё маленькие шажки, пытающиеся сделать императивный React хоть немного декларативным. И даже Vue — продолжение всё тех же идей. Разве не очевидно, что нужно строить нормальное декларативное решение с самого низа?
Если интересно, есть и более радикальные идеи как комилировать реакт в императивный код: github.com/sokra/rawact

А вот обзывать React «костылями» и «уродством» не стоит. Люди используют его в продакшене и довольны, а что вы можете предложить им взамен?
Если интересно, есть и более радикальные идеи как комилировать реакт в императивный код: github.com/sokra/rawact

rawact можно скорее «помянуть» чем упоминать. маловероятно что эту штука когда-либо будет готова взять на себя компиляцию совершенно любого кода на React.

Люди используют его в продакшене и довольны, а что вы можете предложить им взамен?

Очевидно же, Svelte ;-)
Вот, здесь уже можно поговорить и обсудить плюсы и минусы обоих технологий.

А в исходном комменте треда был обычный наброс «фреймворк Х – костыль». Так что дело тут не в обидчивости сообщества а в отсутствии конструктива.
Да, пожалуй. Мне конечно больше импонирует подход как на видео))

Смотря в каком смысле "предложить". Как я уже сказал, настоящая декоративность, если я не ошибаюсь, существовала в Knockout и Angular. Можно было развивать эти идеи. Я думаю, что большинство просто повелось на мнимую простоту Реакта. Для первых шагов было проще понять, как он работает. Но конечная задача все равно гораздо более сложная. И в реальном мире Реакт обрастает кучей всего, что делает этот подход таким же сложным, как Ангуляр, но совершенно тупиковым. Вынос рендера в вебворкер для внесения десятка изменений в DOM этому подтверждение.


А ещё я всем рекомендую посмотреть, как устроен View-слой в уже много лет как полумертвом DerbyJS. Например, там нет костыльного key-property. Когда вы изменяете массив в моделе, на уровне шаблона уже понятно, какие изменения нужно внести в DOM. Зачем ещё что-то сопоставлять по ключу? Хорошая программа (в данном случае фреймворк) строится от интерфейса, а не интерфейс подстраивается под то, как программисту удобнее его реализовать. Имитация декларативности через императивщину — это именно второй случай. Заморачиваться с парсингом декларативного языка долго — проще перезапускать функцию рендера. Но в итоге все равно люди обвешались babel-плагинами.


Большое спасибо за ссылку.

«настоящая реактивность» в первом ангуляре? при этом 40к вотчеров на странице.
На%$й эту вашу «настоящую декларативность» в первом ангуляре.

Я не очень понял, что именно вам не нравится в 40к вотчерах. Проблема Ангуляра была ровно такая же, как и Реакта, только в Model слое, а не View. Ангуляр диффал данные, Реакт — виртуальный DOM. И то, и другое очевидно будет тормозить. Но дифф данных — это более хорошая архитектура. Потому что было несколько способов решить проблемы, оставив тот же подход в View. А Реакт — это шаг назад, потому что рано или поздно его все равно придётся выкинуть и сделать нормальную декларативность.

Я не очень понял, что именно вам не нравится в 40к вотчерах.

В 40к вотчеров мне не нравится 40 тысяч вотчеров. Это тормозит и это убого.

А Реакт — это шаг назад, потому что рано или поздно его все равно придётся выкинуть и сделать нормальную декларативность.

Какая нахер «нормальная деклративность» в браузере?? CSS? Удачи.

Зачем же вы создание 40к вотчеров? Если у вас столько компонентов, то чем это отличается от Реакта, который также будет тормозить?


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

Реактивные подписки, может быть, и есть идеологически более верное решение, но являются ли они решением проблем пользователей? По большому счету, мы пишем на высокоуровневом Javascript в котором происходит очень много всего лишнего, но оправдывает ли это переписывание на ассемблер (WebAssembly в частности)? В большинстве случаев – нет. То же самое и со Svelte.


Но в итоге все равно люди обвешались babel-плагинами.

О чем вы говорите? В стандартом пресете, который используется большинством разработчиков, я вижу только плагины для ES-next синтаксиса и JSX, никакой магии для оптимизации.

Думаю в этом смысле, Svelte берет лучшее из двух миров. Пишем очень декларативно и на очень высоком уровне абстракции, пожалуй, даже выше чем React. Получаем очень низкоуровневый и очень императивный код, который благодаря компилятору и кодогенерации можно считать также достаточно fault-tolerant.

Так-то любой декларативный код будет скомпилирован в императивный)

Пожалуй не любой. Например, есть такие штуки к LitElement, который использует lit-html на template literals и несмотря на то, что для использования тех же декораторов ему все равно нужен препроцессинг, его шаблоны никак не компилируются/транспилируются и остаются условно декларативными в рантайме:

import { LitElement, html, property } from 'lit-element';

export class HelloWorld extends LitElement {
  @property() name = 'World';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}


Тот же JSX, конечно же транспилируется в JS вызовы, но в итоге эти вызовы и сам механизм VDOM нельзя назвать низкоуровневыми.

Поэтому в моем тезисе важно все, Svelte — это именно высокоуровневое и достаточно декларативное написание компонентов с последующей компиляцией их в низкоуровневый и императивный код.
Да уж, за абстрактными словами вы имели ввиду конкретные вещи.) Теперь я вас понял.
<button on:click={handleClick}>
    Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

Это не декларативный код. Не обманывайте, ни себя, ни окружающих.

Поясните, пожалуйста, что тут недекларативного, кроме обработчика события?

Наличие программной логики. А вот привязка обработчика события вполне себе декларативна.

Привязка декларативна, я имел ввиду сам обработчик. Что за "программная логика"?

Да, ваш пример не 100% декларативный, но ведь где-то эта логика должна находиться, правда? Мы можем переписать пример, сделав шаблон более декларативным:

<button on:click={handleClick}>
    Clicked {count} {btnText}
</button>

<script>
  let count = 0;

  $: btnText = count === 1 ? 'time' : 'times';

  function handleClick() {}
</script>


Тут уж кому как удобнее и больше нравится. По-моему мнению Svelte достаточно декларативен в общем смысле и это скорее зависит от того, как писать. treeview, имхо, уже перебор, но я уважаю вашу разработку.

Для полной декларативности не хватает ограничений. До тех пор, пока в фигурные скобки мы можем засунуть любой JS код, мы не сможем воспользоваться преимуществами декларативности. Банально — получить список свойств, от которых зависит шаблон. Или вместо count подставить не текст, а другой компонент. Ну или перевести его на иной язык, где плюрализация делается совсем иначе.

А почему вы уверены, что выражение в скобках может быть любым кодом? DerbyJS, например, парсит джаваскрипт в скобках как декларативное выражение. Вплодь до того, что count + 1 можно присвоить значение 2 и count станет равным единице

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

Банально — получить список свойств, от которых зависит шаблон
Svelte по видимому получает список зависимостей.
Или вместо count подставить не текст, а другой компонент. Ну или перевести его на иной язык, где плюрализация делается совсем иначе.
И это решаемо.

Да и вообще, можно назвать это не логикой, а просто гибким шаблоном, на «for/if/switch» в шаблоне же никто не жалуется и тут тот же «if» (тем более изменение данных не происходит, чисто представление даннных).
Дак это же наоборот хорошо

Мы вроде как не обсуждали вопрос, что хорошо, а что плохо.


Что за преимущества?

Я дальше привёл несколько примеров. Возможность по разному обрабатывать декларации, а не только лишь исполнять логику.


Svelte по видимому получает список зависимостей.

Речь о прикладном коде. Для компилятора-то любой АСТ — не более чем декларации.


И это решаемо.

Каким образом, если у вас в шаблоне зашита конкретная логика вычисления?


Да и вообще, можно назвать это не логикой, а просто гибким шаблоном

Как ни называй, суть не меняется.


на «for/if/switch» в шаблоне же никто не жалуется

Жалуется. Банальный пример: вам нужно в зависимости от флага переставлять две части шаблона местами.

Вот как раз на for/if/switch жалуются, декларативно будет map, match, branch, да просто isOpen && div или тернарик

В примере выше как раз тернарник использовался, но это все равно не достаточно декларативненько по мнению уважаемого vintage.
К описаным вами сомнительным преимуществам декларативности когда в шаблоне не может быть js-выражений добавляется существенный минус — необходимость именования — если раньше можно было указать нужное выражение прямо в шаблоне то теперь придется придумать имя и по этому имени вынести в другое место нужное выражение. А необходимость именования создает точку ручной синхронизации — каждый раз меняя это имя нужно не забыть переименовать в шаблоне (или наоборот). А если потом было решено убрать кусок шаблона то нужно не забыть убрать в другом месте лишние именованные выражения которые были вынесены из шаблона.
Именование хорошо когда оно убирает логическое дублирование кода (когда изменение потребует ручной синхронизации кода в разных местах). В остальных случаях лишнее именование чего либо не было приводит только к усложнению. Например необходимость именовать экшены в редаксе против возможности изменить состояния по месту как mobx-е, или необходимость именовать классы и выносить стили в отдельные местах против возможности указать нужные стили инлайном рядом с тегом или необходимость именовать отдельные роуты в rest-e против возможности вызвать функцию как в rpc. Все это добавляет лишнюю косвенность которая увеличивает количество мест изменения кода в git diff-e а это значит что увеличивается количество неявных связей (программисту нужно держать в голове все места которые требуют изменений при добавлении или изменении/удалении такой-то фичи)

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

Если раньше можно было писать весь код в одном файле, то теперь приходится придумывать имена файлам.


Если раньше можно было писать всё в одной процедуре, то теперь приходится придумывать имена методам.


Если раньше можно было писать всё в одну строчку, то теперь приходится придумывать имена переменным.




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

UFO just landed and posted this here
Дмитрий имеет ввиду, что на 100% декларативным может быть что-то вроде $mol treeview:

$my_hello $mol_view
	sub /
		<= Name $mol_string
			hint \Name
			value?val <=> name?val \
		<= message \


vintage Напишите решение этого примера на treeview для общего развития? Ну и чтобы Balek уже не смог спать спокойно по ночам, зная что такие вещи существуют в нашем мире.

Так тут же дело в абстрактной идеологии. Грубо говоря, сложность программы на Реакте — O(n), хотя проблема решается за O(1). И это заложено в самой архитектуре.

Это в идеальном вакууме.
В реальности обязательно встретятся разработчики которые намудрят с $: синтаксисом (например) и убьют всю производительность.
В реакте хватает систем «защиты от дурака» и предупреждений, а как с этим дела обстоят в Svelte? (хотя это скорее вопрос к PaulMaly)

Я говорю о том, что очень много усилий вкладывается в развитие тупиковой идеи Virtual DOM diff. Если бы последние пять лет сообщество вкладывалось в развитие настоящей реактивности, то были бы защиты от дурака. И, скорее всего, они были бы лучше тех, что есть сейчас. Потому что декларативный код даёт больше возможностей для автоматического анализа, оптимизации и т.п.

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


С другой стороны, пример rawact показывает, что и API react достаточно декларативное для таких оптимизаций, если будет надо. А будет ли надо? Вот не факт, потому что по моему опыту проблемы обычно скрывались в Redux и аналогичных стейт-контейнерах, а не в VDOM. Чинить надо там где сломалось, а не там где это кажется легко

Видимо у нас очень разные определения тупиковости. Потому что мне время (первые минут 30) уже все показало. Например, когда для оптимизации, нужно в shouldComponentUpdate внести то, что и так очевидно из render-функции. Или необходимость указывать key-property при рендере массива. Или перечислять значения вторым аргументом в useCallback. Эти проблемы нужно решать в фундаменте, а не плагинами к бабелю. Тогда будет архитектура, а не набор костылей.

Потому что мне время (первые минут 30) уже все показало

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


В докладе Rich Harris показал один утрированный пример. Много ли пользователей с этим столкнутся? Не факт, я написал довольно много развесистых реакт-компонентов и ни одного shouldComponentUpdate. И без этих приёмников все работает нормально и поэтому костылями мне не кажется

По-моему пример с filtred и useMemo не такой уж и утрированный.
Если бы последние пять лет сообщество вкладывалось в развитие настоящей реактивности, то были бы защиты от дурака.

Сообщество вкладывалось в развитие настоящей реактивности с 2010 года, когда вышел AngularJS, Knockout и Ember. Мне, например, трех лет эквилибристики с scope.$apply хватило, чтобы понять, что «настоящая реактивность» – это как минимум не тривиальная задача.

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

Да, если кратко. Если чуть точнее – указывать, что нужно перепроверить данные на изменение.
Ну и наличие оберток на половину стандартных функций вроде fetch/setTimeout/setInterval, чтобы этого не делать в ответ на AJAX-запрос/таймаут.
В Knockout вам тоже приходилось это указывать?
C Knockout, я не работал дальше туториала. Если там этой проблемы нет, то жаль что взяли первый ангуляр на тот проект, а не его.
Конечно нет. Реализаций реактивности — много разных вариантов.
И у всех свои минусы.
Раз уж меня упомянули))

В реальности обязательно встретятся разработчики которые намудрят с $: синтаксисом (например) и убьют всю производительность.

В теории, возможно. На практике, пока не удавалось, хотя мы активно используем вычисляемые свойства и даже функции. Думаю, что возможно дело в этом:



В реакте хватает систем «защиты от дурака» и предупреждений, а как с этим дела обстоят в Svelte?

Никак, Svelte просто хорошо работает:



Не убедили. «Svelte просто хорошо работает» звучит примерно как «у нас никогда не бывает багов».

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

Согласен, именно так и звучит.))) Но не знаю как еще кратко высказать ту мысль, которую автор Svelte высказывал почти 40 минут. Можно еще наверное так, единственный способ сделать что-то быстрым и надежным — избавиться от всего лишнего. В этом смысле сравнение моторов Теслы и ДВС, имхо, очень показательно.

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

Тут все просто, что-то работает не так как надо, смотрим итоговый код, находим узкое место, пишем issue или PR. Мне кажется это одна из ключевых фишек Svelte. Так как рантайма практически нет (да вот он весь в приципе), то остальное это кодогенерация для каждого конкретного случая и ее всегда можно легко прочитать, понять как работает и придумать как сделать, чтобы работало лучше. В отличии от черных ящиков в рантайме, куда ты передаешь на вход что-то и адская машина VDOM пыхтит и пытается делать свое дело для каждого кейса на свете.
Обращал внимание, что те кто пишут на React довольно обидчивые ребята. Не обращайте внимания.
Интересно, как бы Вы отреагировали, если бы кто-то без всяких обоснований назвал Svetle “костыльным безумием”? Мне это напоминает старый анекдот: “всем хорош Иван Васильевич. Только не любит, когда его по голове бьют”…
Это происходит постоянно, даже в это треде ниже. Вообще если вы посмотрели видео, автор Svelte прекрасно иллюстрирует в чем костыльность React и VDOM в целом. Если вам нравится как это работает — дело ваше. Svelte имеет другой взгляд на довольно очевидные вещи и показывает отличные результаты.
Я к тому, что минусы, скорее всего, за голословную категоричность, а “обидчивость” — свойтсво человека защищать свой основной инструмент от необоснованных (с его точки зрения) нападок.

P.S. Если задача — что-то донести до оппонента, то голословные выпады точно не достигнут цели….
Тут я согласен. Нужно обосновывать. И не так, что «вот мне кажется должно работать так как я привык писать», а «почему это работает не очень оптимально и лучше сделать так».
показывает отличные результаты.

Real world example please.
Типа, вот тут реакт тормозит, а свилт решает.
Любопытно, что по вашей же ссылке есть информация, что эти тесты производительности устарели, уже есть за 2019-й год. Почему вы решили привести более старые и менее актуальные данные? Может потому, что разница в скорости оказалась не такой выдающейся, как хотелось бы?
Может потому что мне этот вопрос задают далеко не первый раз и эта ссылка у меня сохранена ещё с того года?

Посмотрел, ниче принципиально нового в сравнении с реакт не появилось — реакт все также тормозит, ужасает по размеру и кол-ву кода.

Вижу Vue и Angular обновились и немного подтянулись, но там просто какая-то старющая версия Svelte и уже есть планы обновить прилагу до Svelte 3. Тогда и будем смотреть.
Прошу прощения, но мы смотрим по одной и той же ссылке? Потому что я вижу React + Mobx по производительности получил 91 балл, ровно столько же, сколько и Svetle. Так что откуда взялось ваше “всё также тормозит” — совсем не понятно.

Про размер кода — согласен, разница существенная.
Да, сорян, по привычки искал React в конце списка, ну и нашел там React + Redux c 80 баллами.

Вообще, первый пункт всегда был немного tricky. Все проекты располагаются на разные серверах, а меряем мы FMP. Получается очень многое зависит от сервера и от сети. Например, я сам проверил на тех же настройках Lighthouse: и получилось что реализация на React + Mobx получила 69 баллов, а на Svelte 81 балл:





Вполне возможно, что если вы запустите у себя, значения будут другие. Однако очень сомневаюсь что есть вариант, когда React/Mobx получит больше чем Svelte.

Кроме того, есть ведь и другие метрики. В данном сравнении это LoC и Size, но еще есть потребление памяти и время запуска, которое у Svelte находится на уровне VanillaJS реализаций.

На самом деле Svelte не старается быть абсолютно лучшим во всем среди всех, потому что это во-первых это невозможно, во-вторых большинство benchmark'ов — это притянутые за уши кейсы и микрооптимизации. Однако Svelte стремиться быть достаточно хорошим во всем, например, не просаживать память за счет оптимизаций перформанса и т.д., а быть «зелененьким» по всем показателям.

А если мне без разницы КАК это работает? Работает, выдавая нужный результат? Работает. Костыли, наивные прокси ко, или оптимизирующий компилятор с блокчейном и ИИ под капотом — какая разница?

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

У автора Svelte боль в том, что ему приходится много работать с графикой и анимациями и на видео он неплохо показывает, почему React не очень походит для его работы.

Мы первый раз попробовали Svelte на проекте виджета для сайтов, где полностью готовый виджет должен был весить в 5 раз меньше, чем один только React. А также на Smart TV, где VDOM либы довольно быстро посаживают память на дешевых моделях (коих абсолютное большинство рынка).
Это взвешенный подход. Просто создаётся впечатление, что некоторые люди пытаются представить фреймворк Х “серебряной пулей” (или панацеей). И вот такой категоричный подход может вызывать отторжение у большей части аудитории. Гораздо лучше просто акцентировать сильные стороны и указывать области, в которых этот фреймворк лучше подходит, чем категорически заявлять, что он является превосходным во всём…
Я вообще ни разу не фанат переписывания всего и вся на новую свистелку. У нас, например, есть легаси проекты на Ractive и Vue, которые мы понаписали за 5 лет существования студии. Ни мы, ни заказчики не планируем их переписывать на Svelte 3.

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

Кстати, по поводу легаси проектов, есть еще интересный хак со Svelte. Так как он не монолитный и фактически не имеет специфического рантайма, его компоненты довольно просто можно интегрировать в проект на любом другом фреймворке (фактически это как внедрение любой ванильной либы). Так вот, в нескольких проектах на Ractive, нам уже пришлось переписать несколько Ractive компонентов на Svelte, потому что они притормаживали.

Могу сказать, что это сделать довольно просто. Я даже статью на эту тему писал.
Полностью согласен со всеми, кто обвинил меня в резкости. Обычно стараюсь сдерживаться. Но в этой теме меня особенно задевает не то, какой Реакт плохой, а общественная слепота. Повторю, что сказал в первом комментарии: архитектурные проблемы Реакта должны быть очевидны любому программисту с самого первого знакомства. Рич их разжёвывает 30 минут, а в комментариях несколько человек продолжают отвечать, что я «без всяких оснований» ругаю Реакт. Почему это происходит?
Просто никому не нравится когда что-то о чём хорошее мнение поливают грязью, даже если за этим стоят реальные факты. Можно же избегать клише типа «костыльный», особенно если есть, что сказать по делу. Лучше сравнивать и показывать какое ваше решение хорошее.
Согласен, когда React появился и начал поливать грязью AngularJS, Backbone и Jquery, ребята оттуда тоже были не очень довольны. Все это конечно не красиво и вообще фу. Я думаю надо приводить факты, а уж люди пусть сами решают.
Похоже что под «настоящей декларативностью», ты понимаешь «как-то автомагически понимать что надо изменить в DOM».

Ну, пусть так. Тогда задача сводится к вопросу: «как, имея функцию f(x) = y, автомагически получить функцию f(delta(x,x')) = delta(y, y')». Называется это «incremental computing», подробнее тут: en.wikipedia.org/wiki/Incremental_computing

Если эту проблему решить в общем виде, мир станет другим. Например, materialized views в базах данных могли бы автомагически и недорого обновляться, решив почти все проблемы с перформансом на бекенде. Да чего далеко ходить — сам браузер мог бы гораздо эффективнее перерисовывать страничку при изменениях.

Но пока ученые бьются над частичными решениями этой проблемы, в JS-сообществе есть молодые, оптимистичные, и активные ребята, которые такие: «Я гений! Можно ж просто переменным isDirty ставить при записи в них, и все будет ок»! И самое страшное — они успевают написать целые фреймворки, с сайтами про них, конференциями про них. При этом — даже не удосужившись понять почему этот подход не будет работать, даже не попробовав погуглить что говорит по этому поводу наука. Даже не разобравшись почему факапнулись backbone и Angular — с точно такой же концепцией. Даже не попробовав даже на этом фреймворке написать что-то сложнее примеров и todo!

А потом другие молодые, поверившие им люди, начинают писать аппы, попадают на какие-то кейсы сложнее примера с count, начинают трахаться с этой автомагией, раскуривают все внутренности и хитрости фреймворка, героически проблемы решают. И только пройдя этот диалектический цикл, они понимают где и как их кинули.

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

потому с лёгкостью обновляемые инкрементально.

Никто еще не придумал никакого хорошего способа "с легкостью инкрементально" обновлять шаблоны. Все время всплывают какие-то недостатки.

Что вас не устраивает в существующих решениях? Даже если брать старый Ангуляр, там были проблемы с Model, но что было не так с View-слоем? Или чем вас не устраивает Svelte?

Svelte как то может отслеживать ф-ии?
<script>
	let name = 'test';
	const double = () => name + name;
</script>
<input type="text" bind:value={name}> {double()}

Да, конечно, код даже короче, чем вы написали:


<script>
    let name = 'test';
    $: double = () => name + name;
</script>
<input type="text" bind:value={name}> {double()}

Вообще в реактивные объявления можно пихать всё, что угодно, например:


<script>
    let count = 0;
    $: console.log(count)
</script>
<button on:click="{() => count += 1}">+1</button>

Будет в консоль выводить значение count при его изменении

Тут даже функция не нужна:

<script>
  let name = 'test';
  $: double = name + name;
</script>

<input type="text" bind:value={name}> {double}

REPL
А как сделать отслеживание «внешней» ф-ии, типа получения данных из стора и т.п.
Так не работает:
$: now = Date.now();

$: отслеживает лишь изменение переменных из состояния компонента, входящих в соответствующие выражения. Очевидно в Date.now() нет таких переменных. Её вызывать надо, чтобы получить от неё что-то.
Т.е. ваш пример выглядит примерно так:


<script>
    let time = 0;
    setInterval(()=>time=Date.now(),1000);
</script>

{time}

Про работу со стором и функциями в нём лучше расскажет документация

Тут нечего отслеживать, поэтому и не работает. Реактивные декларации работают, естественно, только со известным стейтом.
const getData = () => 'x';
$: double = () => getData() + getData();

Так тоже не работет, видимо в голове нужно будет держать где «computed», а где обычные ф-ии.
Извините, но я никак не могу въехать какой кейс вам нужен. Функции это такие штуки, которые надо вызывать. Вызвал функцию — она вернула значение. Какой смысл следить за объектом функции, что там может измениться? При необходимости можно следить за переменной, которой эта функция где-то возвращает своё значение.
Тут тоже ничего не изменяется, почему double должен как-то изменяться? Svelte использует push-подход и не пересчитывает зазря значения.
Тут тоже ничего не изменяется, почему double должен как-то изменяться?
Оно как минимум должно запускаться, но нет.
Мы можем просто использовать язык
Просто взять и вставить «кусок js» не получится, если у меня вызывается какая-то библиотека в коде, то нужна адаптация.

Не должно там ничего запускаться. В $: должна быть хотя бы одна из переменных стейта. По определению. Тут же умный компилятор не увидел ни одной переменной в выражении и просто не стал включать в бандл ненужный код. Если бы вставили в разметку {double} то компилятор бы ещё и предупредил, что такой переменной нет. $: — это не переменная, чтобы присваивать ей значения функций, это конструкция для перезапуска выражения при изменении входящих в него переменных стейта.

Я понимаю как работает эта часть Svelte, я говорю про другое — если просто взять и вставить рабочий кусок js в svelte — он не заработает*, как мне показалось вначале (когда в некоторых других фреймворках оно работает).

какой тип API был бы лучшим для нас… и поняли, что лучший API — это отсутствие API. Мы можем просто использовать язык
Поэтому «просто использовать язык» — нужно с оговоркой, т.к. это все же «апи».

Но в целом идеи фреймворка очень интересные, посмотрим насколько он станет популярным, думаю первый его конкурент — vue (по типу отслеживания).
Почему не заработает то? Вам почему-то захотелось вставлять именно в `$:`. Может конкретизируете свой пример с реальным кейсом? Попробуем разобраться.
Я не знаю, что вы имеете ввиду под «взять и вставить рабочий кусок js», но попробовал набросать пример Timeago компонента:

<script>
  import { simple as format } from 'timeago-simple';	
  export let date = new Date();
</script>

<h1>Updated {format(date)}</h1>

Рабочий пример

Это похоже на «взять и вставить рабочий кусок js»?
Я про ф-ии которые зависят от контекста, или вы в js только чистые ф-ии пишите?
let a = 1;
const b = () => a + 1;
// const c = () => b() + 1;
$: c = b() + 1;
Очень простой и очевидный пример где функция зависит от контекста («c» зависит от «a» через «b»). Но Svelte это не может осилить, когда тот же ангуляр (да и реакт*) без проблем (как и в нативном JS).

Или вот ещё пример который не работает:
<script>
let user = {name: 'linux'};
let user2 = user;
</script>
<input type="text" bind:value={user.name} />
<h1>Hello {user.name}!</h1>
<h1>Hello {user2.name}!</h1>
Тут нужно вручную привязку делать чтобы заработало (опять же в ангуляр и реакт нет этой проблемы).

Не объясняйте, я знаю почему это не работает (и как исправить), это «болевые точки» всех фреймворков основанных на «observable», я как раз поэтому и привел эти (не единственные) примеры.
И это негативно сказывается в больших проектах. Но все равно Svelte заслуживает что-бы его попробовать.
Я про ф-ии которые зависят от контекста, или вы в js только чистые ф-ии пишите?

Я все же не пойму, почему этот код должен работать? Функцию b никто не вызывает. Да и вообще не понятно, зачем вы тут используете $: он нужен не для таких вещей.

Очень простой и очевидный пример где функция зависит от контекста («c» зависит от «a» через «b»).

А вы вот так попробуйте:

let a = 1;
let b = () => a + 1;
$: c = b() + 1;

Все же const, есть const. Зачем его делать реактивным, если он не может измениться?

Ну а ежели надо, чтобы при изменении a, пересчитывалась b, а за ней и c, тогда опять же так:

let a = 1;
$: b = () => a + 1;
$: c = b() + 1;

Принцип вроде как простой, можно было бы и основить уже.

Но Svelte это не может осилить, когда тот же ангуляр (да и реакт*) без проблем (как и в нативном JS).

Не знаю что там в Angular (кстати вы про 2 или 1? ), но в React никакой «без проблем» и в помине нет. Это код просто будет запускаться каждый раз, поэтому сорян, но это нам не нужно.

Или вот ещё пример который не работает:

То есть вы пишете примеры, которые как вам кажется должны работать, вместо того, чтобы разобраться как это должно работать на Svelte?

<script>
  let user = {name: 'linux'};
  $: user2 = user;
</script>
<input type="text" bind:value={user.name} />
<h1>Hello {user.name}!</h1>
<h1>Hello {user2.name}!</h1>


Мне почему-то кажется вы видео из статьи не посмотрели. Рич там примерно на таком же примере объясняет почему let user2 = user; в JS не может связать 2 переменные. Нету в JS destiny оператора, ну хоть тресни. А в Svelte по-сути есть.

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

Все по той же причине — код будет исполняться заново каждый раз. Собственно Рич Харрис в видео из статьи как раз про это и говорил, а также объяснял зачем мы придумали $:. Лично на мой взгляд если выбирать — исполнять один и тот же код на каждый пчих или написать $: вместо просто let (ваш пример), то для меня выбор очевиден.

это «болевые точки» всех фреймворков основанных на «observable», я как раз поэтому и привел эти (не единственные) примеры.

Сори, но я не увидел «боли». Вы привели сравнение постоянно вычисляемого кода на React/Angular и «лениво» вычисляемого кода на Svelte. Все ваши примеры совершенно просто решаются, поэтому никакой боли не вызывают совершенно.
Тут c не зависит от a через b потому что b не зависит от a.
Зависит — запуситие такой пример на js или любом другом языке — увидите что значение «c» меняется в зависимости от «a». Вообще это очевидно, вы просто пытаетесь оправдать фреймворк.

тогда нужно писать так:
Вот, нужно «адаптировать» и как я уже писал выше:
если просто взять и вставить рабочий кусок js в svelte — он не заработает*
Зависит — запуситие такой пример на js или любом другом языке — увидите что значение «c» меняется в зависимости от «a».

Если один раз запустить, то да, но что-то я не видел, чтобы JS умел отслеживать свойства и строить зависимости.

Вообще это очевидно, вы просто пытаетесь оправдать фреймворк.

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

Вот, нужно «адаптировать» и как я уже писал выше:

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

Если один раз запустить, то да, но что-то я не видел, чтобы JS умел отслеживать свойства и строить зависимости.
c() можете несколько раз запустить, все ранво завист от «a», это же не перменная.
А в DOM должно рендерится актуальное значение, для этого фреймворк и нужен.

А решил все ваши кейсы с минимальными изменениями
Вы подтвердили, что «js код без изменений может не заработать», я не знаю почему вы продолжаете спорить.
c() можете несколько раз запустить, все ранво завист от «a», это же не перменная.

А кто будет перезапускать c(), в кокой момент и по какой причине?

А в DOM должно рендерится актуальное значение, для этого фреймворк и нужен.

Так и есть, все всегда актуально.

Вы подтвердили, что «js код без изменений может не заработать», я не знаю почему вы продолжаете спорить.

Вы так и не раскрыли, что значит «js без изменений». Типа взяли и копипаснули произвольный код откуда-то слева и решили что он должен работать так, как не свойственно js-у? Ну да, так работать не будет. Вы наверно мысли те как в Реакт, мол вот это код будет перезапускаться каждое изменение стейта:
<script>
  let name = 'test';
  const double = () => name + name;
</script>
<input type="text" bind:value={name}> {double()}


но почему он должен, если это все не обернуто в функцию?

Если это просто js на странице (откройте консольку и закиньте его в верстку) сколько раз он отработает и будет ли пересчитана разметка и double при изменении name? Да нет конечно.

Вы можете закинуть любой js код, но должны четко понимать, что так как это не обернуто в функцию, код будет исполнен единожды. Svelte не работает как React, а для отслеживания зависимостей и придумали $:.

Я не понимаю с чем вы тут спорите.
а также объяснял зачем мы придумали $:
Не вы его придумали — это обычный «computed» который есть во многих фреймворках.

Лично на мой взгляд если выбирать — исполнять один и тот же код на каждый пчих или написать $: вместо просто let (ваш пример), то для меня выбор очевиден.
«Красота требует жертв», хотя там и жертв то нет при правильном подходе, вообщем минусы есть в каждом фреймворке — вы просто выбираете те минусы которые для вас менее важны.
Не вы его придумали — это обычный «computed» который есть во многих фреймворках.

Я имею ввиду на уровне нового апи, если вы не поняли. Собственно да, это аналог computed свойств из Svelte 2.

«Красота требует жертв», хотя там и жертв то нет при правильном подходе, вообщем минусы есть в каждом фреймворке — вы просто выбираете те минусы которые для вас менее важны.

А что за такой «правильный подход»? В реакт? Не думаю. Пока не вижу чем я жертвую, когда пишу $: — очень удобно и декларативно.
Поздравляю всех причастных!
С некоторого времени с интересом наблюдаю.
По описанию прям вкуснятина. Обязательно попробую на днях. Скорее всего перепишу раздел настроек в своем расширении для браузера, а то с Vue есть варнинги в Firefox.
без лишних затрат и сложности использования прокси или аксессоров. Это просто переменная.
Зато добавился закулисный вызов функции и компилятор. К инкременту. Если он внутри цикла, лишний код будет в каждой итерации.

let name = 'test';
<input type="text" bind:value={name}>

Объявление переменной может отсутствовать или в нем может быть опечатка.
Будет читаться из window.name со всеми вытекающими.

if (changed.name) {
  set_data(t1, ctx.name);
}

Поскольку changed/$$.dirty в svelte инициализируется не с нулевым прототипом, такие проверки будут протекать, например, обновлять переменную constructor, когда не надо.

Очень сырой фреймворк. Ради минималистичности можно было как раз оставить set() и сделать явный update(), выкинув все остальное, а не наоборот.
Зато добавился закулисный вызов функции и компилятор. К инкременту. Если он внутри цикла, лишний код будет в каждой итерации.

По моему опыту и судя по многочисленным тестам и сравнениям, бандлы на Svelte получаются в целом в 2-3 раза меньше, чем на фреймворках Большой Тройки. Мой собственный опыт можно описать еще проще — когда я заканчиваю писать проект на Svelte, размер итогового кода все еще меньше или сопоставим с размером только лишь фреймворка, типа Vue/React (про Angular вообще лучше промолчать).

Исходя из этого, не думаю что это проблема.

Объявление переменной может отсутствовать или в нем может быть опечатка.
Будет читаться из window.name со всеми вытекающими.

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

Поскольку changed/$$.dirty в svelte инициализируется не с нулевым прототипом, такие проверки будут протекать, например, обновлять переменную constructor, когда не надо.

Надо будет взглянуть подробнее, но если вы считаете что нашли потенциальную багу или эйджкейс — то велком в issues на гитхабе. Уверен ваше замечание может принести большую пользу!

Очень сырой фреймворк. Ради минималистичности можно было как раз оставить set() и сделать явный update(), выкинув все остальное, а не наоборот.

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

Если он внутри цикла, лишний код будет в каждой итерации.
Исходя из этого, не думаю что это проблема.
ecmaeology имел ввиду скорость выполнения, например этот код:
for(let j=0;j<100;j++) i++;
превращается в
for(let j=0;j<100;j++) { i++; $$invalidate('i', i); }

что медленее в на порядок (порядки), но в данном случае это не существенно, вообще похоже нужно отделять переменные которые летят на view от внутренних переменных + computed тоже, нужно держать в голове какие переменные чем являются, ну или префиксы им делать.
что медленее в на порядок (порядки), но в данном случае это не существенно, вообще похоже нужно отделять переменные которые летят на view от внутренних переменных + computed тоже, нужно держать в голове какие переменные чем являются, ну или префиксы им делать.

Как вообще можно сравнивать первый и второй пример, если первый не делает вообще ничего, второй делает вообще все что нужно? Я не понимаю, уж простите.

Это как сравнивать код просто записи значения в переменную и тоже самое + запись значения в БД. Первый будет работать реально быстре, а что толку об этом писать?
Исходя из этого, не думаю что это проблема.
Когда к простейшей операции, особенно в цикле, добавляется неявный вызов функции, речь идет не о размере файла.

Вы привели кусок кода, но видимо не совсем поняли что это такое. Можете объяснить каким образом в этом коде может быть опечатка, если его никто не печатал?
Вы, видимо, сами не совсем поняли, потому что это ваш код, или скопированный вами, из которого удалено лишнее, а потом комментарий к нему. svelte не изолирует переменные из биндов, что является источником дополнительных ошибок и потенциальной уязвимостью.

В реактивном фреймворке реактивность написана невнимательно или некомпетентно.
Если хочется скорости и компактности, выкинуть и писать нормальный JS. Хочется фреймворк — react/vue/angular/другое. Но мнение, конечно, может быть любое.
Когда к простейшей операции, особенно в цикле, добавляется неявный вызов функции, речь идет не о размере файла.

По вашему засинкать стейт и DOM — это простейшая операция? Очень интересно, зачем тогда все эти VDOM? Давайте уж конкретнее тогда.

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


Нет, мой код вот:

<script>
  let name = 'test';
  $: double = name + name;
</script>

<input type="text" bind:value={name}> {double}

А это:
if (changed.name) {
  set_data(t1, ctx.name);
}

Не мой код и в нем не может быть опечаток просто по определению, если вы не поняли.

В реактивном фреймворке реактивность написана невнимательно или некомпетентно.
Если хочется скорости и компактности, выкинуть и писать нормальный JS. Хочется фреймворк — react/vue/angular/другое. Но мнение, конечно, может быть любое.

Уверен вы большой специалист по реактиности и написали кучу фреймворков. Куда уж всем нам до вас. Покажете свои решения?

По поводу Svelte — работает прекрасно, баги бывают крайне редко. Говорить вы можете сколько хотите, но мы Svelte практикой проверяем и она говорит об обратном.

По вашему засинкать стейт и DOM — это простейшая операция
Простейшая операция — инкремент.

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

По поводу Svelte — работает прекрасно, баги бывают крайне редко. Говорить вы можете сколько хотите, но мы Svelte практикой проверяем и она говорит об обратном.
Сайт вашей конторы говорит, что там лапша из смеси голого JS и jQuery.
Простейшая операция — инкремент.

Если это изменение должно быть засинкано в DOM, то операция становится в разы сложнее, а если нет, то $$invalidate выстреливает вхолостую. Опять же тесты и практика не подтверждают ваши слова:



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

Хм, действительно, я тут не совсем понял как вы построили высказывание. Подумал, что вы на полном серьезе считаете что в сгенерированном коде могут быт опечатки )))))

Ваше замечание интересно в этом контексте, пожалуй, я даже заведу issue на этот счет, чисто для интереса. В любом случае, если вы сделаете опечатку, компилятор подскажет вам об этом в виде сообщения:

'name' is not defined (5:11)

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

Кроме того вот это ваше высказывание не совсем корректно:

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

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

Сайт вашей конторы говорит, что там лапша из смеси голого JS и jQuery.

Фу, как не красиво. Чтобы вы знали, этому сайту уже больше 5-ти лет и визуально он до сих пор довольно актуален. За это время он ни разу не менялся, потому что не было необходимости — заказов итак хватает. Однако как раз сейчас идет переезд на Svelte/Sapper и перевод на английский, ибо думаем активнее выходить зарубеж.

Ваших инновационных и «компетентных» проектов мы видимо не увидим тут.
Если это изменение должно быть засинкано в DOM, то операция становится в разы сложнее
textNode.nodeValue = count
Невероятная сложность.

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

компилятор подскажет
В одной ситуации подскажет, в другой нет. Насколько целесообразно в вопросах надежности и безопасности кода полагаться на предупреждения компилятора?

Svelte этого не делает, потому что это делает бандлер. Не уверен, что вообще существует кейс, когда код компонента будет просто выброшен в глобал
Баг совсем не в том. И слово бандлер не спасет. Вот такой «компонент» svelte:

<script>
let namе = 'test';
$: double = name + namе;
</script>
<h1>{name}</h1> {double}

Подключается так:

import App from './App.svelte';
export default new App({
  target: document.body
});

Он скомпилируется и будет читать заголовок из window.name. Проброс через переменные верхнего уровня компонента — архитектурная ошибка того же начального уровня, что и остальные.

как не красиво
Сами виноваты, пытаетесь увести дискуссию на другие фреймворки и мифическую практику, хотя ни то, ни другое с рациональной точки зрения к теме не относится. В фреймворке ошибки. Они не исчезнут ни от рассказов про сто лет использования, ни от обсуждения косяков в другом коде. Хотя автор наверняка молодец и хорошо потрудился, но использовать такое и в существующем виде незачем.
Невероятная сложность.

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

Это называется не «тесты и практика», а картинка, бессмысленность которой понимает даже автор обсуждаемого «фреймворка».

Для теста вполне сойдет, так же как и другие benchmark'и и сравнения, которые доступны в интернетах.

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

В одной ситуации подскажет, в другой нет. Насколько целесообразно в вопросах надежности и безопасности кода полагаться на предупреждения компилятора?

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

Баг совсем не в том. И слово бандлер не спасет. Вот такой «компонент» svelte:

Еще раз, на практике таких багов не бывает, потому что компилятор всегда сообщит о такой проблеме.

Сами виноваты, пытаетесь увести дискуссию на другие фреймворки и мифическую практику, хотя ни то, ни другое с рациональной точки зрения к теме не относится.

Да, я вижу вы теотерик и практика для вас лишь миф.

В фреймворке ошибки. Они не исчезнут ни от рассказов про сто лет использования, ни от обсуждения косяков в другом коде. Хотя автор наверняка молодец и хорошо потрудился, но использовать такое и в существующем виде незачем.

Ошибки есть везде. 484 открытых issue в React, 192 в Vue и 2370 в Angular. И что это за показатель? Видите проблему — откройте issue, ее исправят, а вам скажут большое спасибо. Так работает open-source.
Внутри Svelte примерно это и делает
Вместо одной проверки и присваивания он запускает сгенерированную портянку, которая линейно сканирует все отслеживаемые переменные и свойства на изменения. Если отойти от уровня примеров и завести в компоненте простенькие структуры, при изменении свойства объекта, «фреймворк» опять же целиком инвалидирует объект (похоже что и вызываемая при инвалидации функция сравнения некорректна, всегда возвращает true для объекта или функции) и тоже запускает портянку.

Вставленная инвалидация:
obj.prop = 'value'; $$invalidate('obj', obj);

Функция сравнения:
function safe_not_equal(a, b) {
  return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}

Является ли дискуссионным, что нормальные функции сравнения могут выглядеть по-разному, но не так? Такое невозможно использовать сколько-нибудь продолжительное время и не напороться.

вы теотерик и практика для вас лишь миф
Раз уж продолжаете настаивать
Мы используем
На практике, пока не удавалось, хотя мы активно используем
По моему опыту
мы всегда внимательно относимся
тесты и практика
Практика — это непосредственно моя, моей компинии
на практике таких багов не бывает
вы теотерик и практика для вас лишь миф
Ненормально частые упоминания «практики», не сопровождаемые конкретикой, вызывают подозрения, что у вас какие-то серьезные проблемы с практикой. И вот эти высокомерные указания комментаторам темы (volch/jsmitty/lega) то нажимать кнопочки, то смотреть материалы и преждевременные предположения, что именно они чего-то не поняли, с чем-то незнакомы или не разобрались.

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

Точно хотите гнуть такую линию дальше?

Видите проблему — откройте issue, ее исправят, а вам скажут большое спасибо. Так работает open-source.
Если что-то является решетом, от архитектуры до реализации, зачем такое использовать.
Вместо одной проверки и присваивания он запускает сгенерированную портянку, которая линейно сканирует все отслеживаемые переменные и свойства на изменения.

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

Для примера, возьмем ваш пример из первого сообщения, но для наглядности добавил еще и вывод:

<script>
  let name = 'test';
</script>

<input type="text" bind:value={name}>
<h1>{name}</h1>


Для вашего удобства даже завел REPL.

Читаем код и отмечаем основные моменты:

1. При создании компонента с помощью хелперов над DOM API создаются все нужные элементы и вешается обработчик события oninput на поле:

input = element("input");
t0 = space();
h1 = element("h1");
t1 = text(ctx.name);
attr(input, "type", "text");
dispose = listen(input, "input", ctx.input_input_handler);

Все это дешево, максимально просто и эффективно.

2. Весь скоуп компонента на самом деле выгдядит так:

function instance($$self, $$props, $$invalidate) {
	let name = 'test';

	function input_input_handler() {
		name = this.value;
		$$invalidate('name', name);
	}

	return { name, input_input_handler };
}


Функция $$invalidate, к которой вы так прицепились, на самом деле не делает практически ничего. Она делает строгое сравнение значений (причем как примитивов, так и объектов и функций) и если значение поменялось, помечает его как dirty и планирует микротаску для обновления в конце тика. Опять же супер дешевые операции, поэтому вызывать ее можно когда угодно, хоть в циклах, хоть где.

3. Когда запускается микротаска, выполняется следующий код (внимание, это вся необходимая работа с DOM):

if (changed.name && (input.value !== ctx.name)) input.value = ctx.name;
if (changed.name) {
  set_data(t1, ctx.name);
}


Который не просто дешевый, он ничего не стоит. Все ссылки на все элементы уже сохранены, в changed только инвалидированные за тик вещи.

set_data — это такое же DOM API хелпер:

set_data(text, data) {
  data = '' + data;
  if (text.data !== data) text.data = data;
}


А теперь уж совсем для простоты, работает это так:

1) Вводим значение в поле для ввода
2) В конце тика последнее значение ввода пишется в data заранее сохраненного элемента.
3) PROFIT

Я не знаю что может быть проще и эффективнее. Тут даже querySelector не делается, не говоря уже о пересоздании всех структур в React и render/reconcile VDOM.

Если отойти от уровня примеров и завести в компоненте простенькие структуры, при изменении свойства объекта, «фреймворк» опять же целиком инвалидирует объект (похоже что и вызываемая при инвалидации функция сравнения некорректна, всегда возвращает true для объекта или функции) и тоже запускает портянку.

Абсолютно нормальная проверка или вы предлагаете бегать вглубь объекта и проверять все его свойства? Если вы не в курсе в JS нет способа понять, что какое-то свойство внутри объекта, изменилось.

Является ли дискуссионным, что нормальные функции сравнения могут выглядеть по-разному, но не так? Такое невозможно использовать сколько-нибудь продолжительное время и не напороться.

Ой, да что вы говорите))) Тогда я вас добью окончательно. Если в том же REPL вы выставите опцию immutable: true (которую кстати мы всегда используем), то проверка станет еще проще:

function not_equal(a, b) {
  return a != a ? b == b : a !== b;
}

Приколите, а мужики то не знали что так можно, да?

Ненормально частые упоминания «практики», не сопровождаемые конкретикой, вызывают подозрения, что у вас какие-то серьезные проблемы с практикой.

Практика — это то чем я занимаюсь применительно к Svelte. Уверен, в данном треде, к сожалению, не многие могут говорить с точки зрения практики о сабже. Вы и многие другие, можете только теоретизировать и хаять со стороны, а я и моя команда пишем на Svelte уже почти 2 года реальные проекты, поэтому именно «практика» то, чем я могу поделиться, в отличии от вас. Вы в данном случае не более чем теоретик. Когда напишете хотя бы парочку проектов на Svelte, тогда и пишите разгромную статью как все плохо работает и какие есть проблемы. От тех, кто уже попробовал Svelte я лично пока жалоб не слышал.

И вот эти высокомерные указания комментаторам темы (volch/jsmitty/lega) то нажимать кнопочки, то смотреть материалы и преждевременные предположения, что именно они чего-то не поняли, с чем-то незнакомы или не разобрались.

Весьма стандартный ход, кивать на других, когда своих аргументов нет. Со всеми уважаемыми коллегами, которых вы упомянули, у нас совершенно конструктивный диалог в треде. А про «кнопочку», которая вас так зацепила, меня спрашивают далеко не первый раз, когда поверхностно относятся к туториалу.

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

Я вам выше писал, что кое-что из этого я не считаю проблемой, кое-что еще требует исследования и возможно issue. Но в целом, это не более чем ваши придирки. 2К issue в трекере Angular почему-то не делают его «сырым» по вашему мнению.

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

Вы вырываете из контекста код и потом тычите им, как будто все видели то, что видели вы. Более того, со странными формулировками, типа «лишний код будет в каждой итерации» Что за лишний код? Почему он лишний? Я вот так и не нашел, где в каждой иттерации добавляется какой-то лишний код. Давайте уж тогда пример, а не просто бла-бла. Тогда и будет говорить предметно, а пока только «если бы да кабы».

Что-то вы как раз не демонстрируете даже базовых практических знаний. Тогда становится понятно, зачем нужен генератор JS.

А вы не демонстрируете даже базовых навыков адекватности. Кто вы, что вы из себя представляете, никто не знает. Про половину коллег из треда хоть что-то известно. Можно с ними соглашаться или нет, но каждый, кто высказывается тут конструктивно, имеет опыт, в том числе в open-source. А где ваши проекты? Нет, не видели. Зато слышали что все вокруг идиоты. Может быть, какие-то доклады/труды/статьи? 1 штука на Хабре и та «без комментариев». На Хабре с этого года, а где раньше были? Вы может школьник-троль какой-то? Тогда все в целом становится на свои места.

Точно хотите гнуть такую линию дальше?

О да, могу гнуть вас дни на пролет.

Если что-то является решетом, от архитектуры до реализации, зачем такое использовать.

Ну так, свою реализацию в студию. Ну или хотя бы что-то чужое, что вы считаете рабочим. Посмотрим, разберем по полочкам. К сожалению, но умничать со стороны всегда проще.
Если изменения происходят чаще 60 раз в секунду (а это многие мышиные события, например), то делать рендеринг на каждое событие — не эффективно. Обновление зависимых состояний (в том числе и рендеринг) нужно делать по requestAnimationFrame. Но текущая модель реактивности Svelte такое сделать не позволяет. Чтобы это стало возможным и не приводило приложение в неконсистентное состояние, кроме инвалидации при изменении, нужно добавить ещё и ревалидацию при чтении.
Удивительная штука, знаете. Казалось бы, логически вы правы, но ведь оно все работает.

Тут ведь как получается, одни выдвигают гипотезы и пытаются стоить производительные решения на Virtual DOM, другие выдвигают другие гипотезы и делают штуки вроде Incremental DOM. В этом же время, у Svelte другая гипотеза, а что если просто убрать все лишние абстракции и тупо использовать DOM?

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

Я это называю «augmented intelligence for web apps» — когда человек делает то, что у него хорошо получается на высоком уровне абстракции, а машина делает то, что хорошо получается у нее.

По поводу, requestAnimationFrame, так и есть. Микротаски конечно же запускаются асинхронно, а не сразу как произошли изменения в стейт.

Помню когда у React появились Fibers, они как бы решали как раз такие проблемы и была еще демка с треугольниками Серпинского, типа вах у нас теперь такое работает. Но на Svelte это просто работает и все. И не надо туда никаких Fibers и shouldComponentUpdate.

Вот фигня какая.
По поводу, requestAnimationFrame, так и есть. Микротаски конечно же запускаются асинхронно, а не сразу как произошли изменения в стейт.

То есть следующий обработчик события будет оперировать не консистентным состоянием, если он успел вызваться раньше следующего фрейма. Это как раз то, что что все так "любят" первый Ангуляр.

Почему же? Асинхронные только изменения в DOM, а не стейт. Не уверен что в AngularJS были микротаски.

Ну а остальные вычисления (все эти "автораны" в ${}) получается будут зазря отрабатывать синхронно. Модель реактивности с "ячейками" позволяет откладывать вычисления на неопределённый срок, пока кому-то не потребуется результат.

Модель реактивности с «ячейками» позволяет откладывать вычисления на неопределённый срок, пока кому-то не потребуется результат.

Не спорю. Уверен внутри $mol все сделано очень хорошо. Несмотря на то, что мы обычно друг другу апеллируем, а я иногда слегка тролю $mol, но я уверен, что вы профессионал и вижу что часто говорите толковые вещи. Доклады ваши понравились.

Не исключаю, что в $mol какие-то вещи внутри реализованы лучше чем в Svelte, хотя и концепт разный совсем. Лично для меня основная проблема $mol — это присловутый DX. В этом смысле Svelte для меня выглядит значительно проще и понятнее, но это, сами понимаете, на любителя.
Тут шоу куда веселее: стейт тоже обновляется асинхронно.

svelte.dev/repl?version=3.1.0&gist=38667c3b7f224c1187e0dbb6238bee52

PaulMaly почини мой gist, если я еще не понял как объяснить мысль svelte.
Предположение было такое, что sheep всегда держит актуальное состояние i.
Поправил немного, для более корректной работы: https://svelte.dev/repl?version=3.1.0&gist=5eb0ed7cce0b4debbafca33921a9ab8a

По-моему это поведение никак не связано со Svelte. В каждую итерацию setInterval кэшируется значение sheep. Во внешнем коде значение sheep изменилось, но внутри коллбека нет. Когда через секунду коллбек вызывается заново, туда просачивется новое значение sheep. За это время значение i внутри коллбека меняется 5 раз. Поэтому значение в консоле мы видим 5 значений i на одно значение sheep.

Чтобы убедиться в этом, можем написать еще один лог:
svelte.dev/repl?version=3.1.0&gist=4187edba9cbf8270c2d1919c7f453b22
Хмм, ну вот тут переосмысленная реактивность еще хитрее прошлых подходов.

В условном mobx \ vue не надо ждать requestAnimationFrame чтобы computed пересчитался
(понятно, что я могу вооружиться геттерФункцией + memoize one + ее вызовом в $: ($: чтобы не тащить логику в шаблон). И добиться такого же поведения в svelte, но это уже весьма неудобно.
В условном mobx \ vue не надо ждать requestAnimationFrame чтобы computed пересчитался

Вы что-то путаете по-моему. Асинхронные в Svelte только обновления DOM. Собственное точно также как и в Vue. Это сделано вот почему:

import App from './App.svelte';
const app = new App({ target });

app.foo = 1; // мы не хотим чтобы DOM обновился сразу
app.bar = 2; // а потом еще раз


Тоже самое будет в Vue, так как там ипользуются аццесоры/прокси. Другое дело в Svelte 2 все обновлялось синхронно, потому что стейт обновлялся только так:

app.set({ foo: 1, bar: 2 });


То что вы описали с setInterval — это просто обычное поведение JS-а.

Тут речь про такой кейс:


class Store {
    @mem val = 1
    @mem get qube() { return val ** 3 }
}

const store  = new Store
console.log( store.qube ) // 1
store.val = 2
console.log( store.qube ) // 8
Но это можно сделать на js без всяких фреймворков.
Можно подумать фреймворки не на js пишутся.
Абсолютно нормальная проверка или вы предлагаете бегать вглубь объекта и проверять все его свойства? Если вы не в курсе в JS нет способа понять, что какое-то свойство внутри объекта, изменилось.

А можете пояснить как cd для структур работает? Вот я передал объект в пропсы, объект поменялся, запустилась ваша invalidate, но ее же теперь надо запустить для всех полей во вложенных компонентах? Это как все осуществляется?

Немного абстрактно напишу, как я себе это представляю, так как я предпочитаю под капот не лазить. Работает да и ладно… (С настоящей машиной также поступаю =))
invalidate помечает, что изменился объект, не нужно метить все поля по отдельности.
Например, при сборке компонента компилятор нашёл, что в разметке и в коде внутри <script> из всего объекта используется только поле obj.name. Так при инвалидации obj Svelte и проверяет только изменилось ли значение obj.name, весь объект целиком его не интересует.

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

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

Ну это как-то в данном случае не особо согласуется с "точечными изменениями где надо" и все такое.

Отнюдь, Svelte точно знает «где надо» — благодаря статическому анализу при компиляции, а всё что выше это не про «где», а про «когда».
Отнюдь, Svelte точно знает «где надо»

Откуда? Вот я взял и написал obj1[field1] = obj2[field2] и потом использовал первое поле в шаблоне, никакого способа определить статически это не существует.

Почему? У вас первое поле в шаблоне(или присваивание ему в коде) — что тут сложного при компиляции найти эти моменты и вставить в бандл соответствующие проверку изменения obj.field1 и действие при инвалидации obj?


<script>
    let obj = {
        field1: "property #1",
        field2: "property #2"
    }

    setTimeout(()=>obj['field1']=obj['field2'],3000);
</script>

{obj.field1}

REPL

что тут сложного при компиляции найти эти моменты

При компиляции этих моментов еще нет. Искать просто нечего.


setTimeout(()=>obj['field1']=obj['field2'],3000);

Ну у вас литералы, а если так:


setTimeout(()=>obj[this.someButtonClicked ? 'field1' : 'field2']=obj[this.someButtonClicked ? 'field2' : 'field1'],3000);

А что изменится то?


<script>
    let obj = {
        field1: "property #1",
        field2: "property #2"
    }

    let someButtonClicked = true;

    setTimeout(()=>obj[someButtonClicked ? 'field1' : 'field2']=obj[someButtonClicked ? 'field2' : 'field1'],3000);
</script>

{obj.field1}

REPL


Компилятор знает, что obj -это часть стейта, поэтому он ищет в коде и в разметке все места где грубо говоря объекту или его свойству что-то присваивают и в бандле добавляет после присваивания сначала ; — а потом $$invalidate('obj', obj);. Посмотрите вкладку JS Output в REPL.

А что изменится то?

Изменилось то, что вы не знаете, какое поле поменялось. И узнать не можете, до рантайма.


Компилятор знает, что obj -это часть стейта, поэтому он ищет в коде и в разметке все места где грубо говоря объекту или его свойству

Ну то есть, в общем, как я и говорил — полноценный цд, при котором надо пройти абсолютно по всем переменным шаблонов всех компонент в поддереве и проверить, не менялись ли они.
Ну или надо делать полноценный ререндер всего поддерева, как в реакте.

По всем ходить не надо, а только по тем кто был инвалидирован. Места, где объект инвалидируется вычисляются при компиляции.

Изменилось то, что вы не знаете, какое поле поменялось. И узнать не можете, до рантайма.

Вы код же видели? Он же работает. Компилятору не надо знать, какое поле меняется, ему надо знать, что объект меняется.
По всем ходить не надо, а только по тем кто был инвалидирован.

Так вы не знаете, кто был инвалидирован. Поменяли какое-то поле obj — с-но, абсолютно любая переменная в темплейте из поддерева могла измениться, нет никакого способа узнать, какая именно.
Значит, вам надо перебирать все эти переменные и проверять, изменились они или нет. Другие варианты отсутствуют.


Он же работает. Компилятору не надо знать, какое поле меняется, ему надо знать, что объект меняется.

Толку от этого знания? Обновлять-то ноду, которая биндится на поле, а не на объект.
Ну либо перерисовывать все поддерево на каждый чих (именно ради того, чтобы этого не делать, в реакте и вкостылили вдом).

Так вы не знаете, кто был инвалидирован.

Инвалидирован был объект. При проверке отсекаются другие объекты стейта, которые не были инвалидированы.


Значит, вам надо перебирать все эти переменные и проверять, изменились они или нет.

Так мы же, вроде, уже это обсудили сильно выше. Да все поля объекта, встречающиеся в данном контексте переберем.


Помните я писал про "суперпупердешёвые" операции:


if (changed.obj) && t2_value !== (t2_value = ctx.obj.field1) set_data(...);

При этом проверка свойства объекта по ресурсоёмкости ровно такая же ничтожная, как и проверка обычной переменной в стейте. Гораздо более ресурсоемкий рендер точечно произойдет по команде set_data() при истинности вышеуказанного выражения.

Так мы же, вроде, уже это обсудили сильно выше. Да все поля объекта, встречающиеся в данном контексте переберем.

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


Или все же под "контекстом" подразумевается именно только текущий шаблон, и тогда в описанном вами условии заведомо true и с-но постоянно происходит перерендер всей страницы? Какой из этих двух вариантов?

и тогда в описанном вами условии заведомо true

не понял, почему true?


  • changed.obj — смотрим менялся ли объект — либо true либо false
  • t2_value !== ctx.obj.field1 — смотрим не равно ли прошлое значение текущему значению поля объекта — либо true либо false

то есть надо будет гонять цд по факту по всему дому, по всем стопицотыщам нод

Извините, я не знаю что такое "цд". Но нигде гонять мы не будем ни в DOM, ни по нодам, ни даже по объекту changed. Итерирование вообще сильно дороже выходит, чем аналогичное количество булевых if-ов.


При каждом тике просто запускается проверка всех "суперпупердешёвых" условий, подавляющее большинство из которых отваливается ещё на его части (changed.obj) — оно не просто так в скобочках.


На счёт контекста я не знаю — либо в компоненте — либо в иерархии компонентов. Но по сути это ничего не меняет — всё тот же набор if-ов для всех возможных переменных во вьюшке.

Итерирование вообще сильно дороже выходит, чем аналогичное количество булевых if-ов.
Но ведь количество булевых if-ов не аналогичное, не так ли?

А вот я не готов реально сравнивать одну итерацию по объекту с одним булевым сравнением. Как я выше написал, я под капот лазить боюсь, знаний хватает только на уровень абстракции фреймворков. Но логика подсказывает, что одна итерация это минимум операция сравнения и операция присваивания(не считая действий внутри цикла). Вроде бы то же самое, что делает Svelte в if-ах.
Но я думаю, есть причина почему создатели Svelte не итерируются по changed, а используют if-ы. Может быть итерации даже более дорогостоящие, чем я себе представляю. Мне на самом деле было бы интересно в этом разобраться, надеюсь, как и вам.

А вот я не готов реально сравнивать одну итерацию по объекту с одним булевым сравнением
Ок, ну и не будем. Вообще такие вещи можно делать через массив, в который добавляются изменения, цикл по массиву недорогой.
Против if-ов будет играть как минимум их количество и то, что они будут часто промахиваться мимо кэша и дергать два прототипа.
Вообще такие вещи можно делать через массив, в который добавляются изменения

Мне вот тоже кажется, что логичнее делать перебор по changed (тем более если бы это был массив, а не объект). Он бы большую часть времени пустой был, если на сайте нет чего-то постоянно меняющегося. Но у контрибьютеров Svelte свое видение. И не дураки, чай. Стало бы понятнее, наверное, если расковырять как Svelte работает. Или хотя бы спросить их на эту тему. Я уверен у них есть железные аргументы.

И как мне кажется причина эта очень простая — кроме проверки самого changed, у нас могут быть еще дополнительные проверки для того, чтобы «решить» что DOM манипуляция действительно нужна. Поэтому общий обход по объекту changed не исключит необходимости иметь if/switch внутри этого обхода, но при этом в итоге будет не только дороже, но и визуально хаотичнее.
не понял, почему true?

Ну объект поменялся.


При каждом тике просто запускается проверка всех "суперпупердешёвых" условий

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


Вы видимо не понимаете о чем речь. Вот есть у вас компонент foo в котором есть объект obj который прокидывается пропсом в темплейте:


<bar yoba={obj}></bar>

и вы меняете поле в этом obj: obj.field1 = 'yoba'.
У компонента bar тоже есть темплейт и там что-то биндится. В этм теплейте — тоже вызываются какие-то компонеты (в них другие и т.д.) и в них тоже что-то биндится.
Так вот — как только вы изменили поле внутри obj (или сам obj), то любая забинженная внутри темплейта bar или внутри какого-либо другого темплейта (рекурсивно и до самого конца) переменная могла измениться. Вы никак не можете заранее скзаать какая — могла любая.
И у вас два варианта:


  1. вы просто перерендериваете весь дом для bar целиком (на самом деле в итоге вы перерендериваете практически весь сайт каждый разпри таком подходе) — подход ангуляра
  2. вы перебираете поочередно все биндинги и смотрите, какие из них изменились, делая точечные апдейты (при этом вы не можете заранее написать сгенерировать портягу ифов, т.к. не знаете какие у вас там в рантайме будут ноды, сколько их и т.д.) — подход реакта

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


так какой из этих вариантов в svelte реализован?

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


  1. Вариант точно нет, Svelte не бегает по DOM в рантайме.
  2. Похоже на него, за исключением того что именно портяга if-ов и сгенерирована. Я всё еще не могу понять, почему нельзя их написать при компиляции. Тут очень просто же всё — если переменная в стейте (let name; или let obj;) и если она где-то есть в разметеке ({name} или {obj.foo}) — добавляем if. Это не при запуске скрипта делается, это делается компилятором при анализе текста файла компонента.Таким образом получается набор if-ов для каждого отдельного компонента. Знать где и сколько будет или не будет нод в рантайме вообще не важно — подход Svelte.

Дальше, я думаю, в рантайме все-таки рассматриваются контексты компонентов по отдельности — если компонент не рендерится в текущем тике, то набор его if-ов, соответственно, не пробегается. Для тех, кто рендерится, пробегается весь набор if-ов и делаются соответствующие манипуляции в DOM при выполнении условий. При этом они также собираются в микротаски и оптимизируются.

Я всё еще не могу понять, почему нельзя их написать при компиляции.

Потому что они неизвестны при компиляции.


Тут очень просто же всё — если переменная в стейте

Если у нас целый объект, то мы не знаем, какие его поля в стейте — а какие нет. Кроме того — мы не знаем, на какие ноды биндятся эти поля, так что даже если бы мы знали, например, что изменилось поле Х — то этого не достаточно для апдейта dom. Надо еще знать, в какую ноду этот Х данные сует (и таких может быть не одна).


если компонент не рендерится в текущем тике, то набор его if-ов, соответственно, не пробегается

Так чтобы узнать рендерится он или нет в текущем тике, надо узнать, изменились или нет биндинги :)

Объясните же мне наконец, зачем нам нужно знать структуру объекта. Я вас уже просил — откройте REPL из моих примеров и посмотрите JS Output. Ну и вот я еще один пример набросал — тут ну вообще во время компиляции про структуру объекта ничего не известно, неправда ли?


<script>
    let obj = {};
    let n = 5;
    for(let i=0; i<10; i++){
        obj['field'+i] = 'test'+i
    }
</script>

{obj['field'+n]}

REPL


Я уверен, вы больше меня разбираетесь в Javascript, не поленитесь посмотрите те несчастные 60 строк скомпилированного кода приложения, и может быть вы лучше расскажете как это всё работает.

Ну и вот я еще один пример набросал — тут ну вообще во время компиляции про структуру объекта ничего не известно, неправда ли?
Тут весь «obj» будет инвалидирован, не важно что вы там поменяли.
Т.е. берется корневое имя переменной, например если вы меняете только «obj.user.status.name», то инвалидируется весь «obj», поэтому ваш пример будет нормально работать.
А он всегда корневой будет инвалидирован, да. Но инвалидирован в случае Svelte не означает поиск измененных полей в объекте или же беганье по DOM/VDOM. Он вообще практически ничего не делает, просто говорит «хей, похоже эта часть стейта как-то изменилась. может надо обновить DOM?»

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

А нам нужно знать структуру объекта до компиляции, чтобы обновить DOM?

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

Разве шаблон нам не говорит об этом?

Так чтобы узнать рендерится он или нет в текущем тике, надо узнать, изменились или нет биндинги :)

Именно это делает $$invalidate, говорит рантайму «хей, похоже эта часть стейта как-то изменилась. может надо обновить DOM?»

А нам нужно знать структуру объекта до компиляции, чтобы обновить DOM?

Не нужно, если вы делаете cd, перерендериваете все поддерево или просто игнорируете это изменение. В противном случае — нужно.


Именно это делает $$invalidate, говорит рантайму «хей, похоже эта часть стейта как-то изменилась. может надо обновить DOM?»

Но у вас инвалидируется весь объект. То есть invalidate говорит рантайму: "хей, где-то на вашем сайте что-то поменялось!"
Чтобы это что-то поменять, вам надо либо найти точку изменения теперь (и для этого надо перебрать все биндинги, т.к. измениться мог любой), либо перерендерить весь сайт, либо ничего не делать. Какой из вариантов — вариант svelte?

Извините, но у вас слегка рантайм мышление. К сожалению, это свойственно большинству и в целом нормально, потому что привычно. Без обид.

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

Теперь по-существу.

1) Нужно ли знать структуру объекта до компиляции, чтобы обновить DOM? Рассмотрим пример:

// Компонент

<h1>{obj.foo}</h1>
<h2>{obj.baz.baz2}</h2>

<script>
    export let obj = {};
</script>

// Использование

<Component {obj} />

<script>
  import Component from './Component.svelte';
  let obj = {
    foo: 1,
    bar: 2,
    baz: {
       baz1: 3,
       baz2: 4
    },
    qux: 5,
    // сколько угодно еще полей
  };
</script>

Вопрос: нужно ли нам знать о всех +100500 полях объекта, чтобы обновить DOM компонента? Ответ: не нужно.

Теперь про это:

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

2) Как собственно обновить DOM точечно, не чекая всего и вся? Давайте попробуем представить, что мы просто пишем код примера выше:

const target = document.body;
const obj = { ... };

let t1_value = obj.foo, 
  t2_value = obj.baz.baz2;

const h1 = document.createElement('h1'),
  t1 = document.createTextNode(t1_value),
  h2 = document.createElement('h2'),
  t2 = document.createTextNode(t2_value);

target.appendChild(h1);
h1.appendChild(t1);
target.appendChild(h2);
h2.appendChild(t2);

Грубо, но как-то так в целом. Теперь, в каком-то момент мы понимаем что объект изменился (присловутый $$invalidate), а точнее изменились какие-то его поля. Точно знать какие именно поля изменились нам не нужно, так же как чекать все эти поля.

Как бы мы написали обновление DOM, если бы писали не рантайм абстракцию на все случаи жизни, а делали все по месту, точно в зависимости от текущего кода (внимание $$invalidate в Svelte лишь отмечает какой кусок стейта изменился, пример ниже утрирован для простоты)?

function invalidate(changed) {
  if (changed.obj && t1_value !== (t1_value = obj.foo)) {
     t1.data = t1_value;
  }
  if (changed.obj && t2_value !== (t2_value = obj.baz.baz2)) {
     t2.data = t2_value;
  }
}
...
obj.baz.baz2 = 10;
obj.qux = 11;
invalidate({ obj: true });

Итак, в итоге мы сделали изменение вложенного поля объекта, который присутствует в DOM и еще одного, который там не используется. Получили точечное изменение DOM только для соответствующего элемента. Так как значение obj.foo не поменялось, эта часть DOM осталась без изменений. Несмотря на то, что поменялся obj.qux, никаких dirty checking или reconcile на это изменение не происходит.

Теперь расмотрим кейс, когда этот же объект прокидывается дальше, в следующий компонент. Собственно тут даже рассматривать нечего. Все будет тоже самое, только условная функция invalidate для вложенного компонента будет другая. Но принцип работы будет тот же — компонент получает «сигнал», что объект как-то изменился и проверяет изменились ли значения ТОЛЬКО тех полей, которые используются в той части DOM, за которую он отвечает. Если значения не поменялись, тогда ничего не происходит.

И еще раз основная мысль — когда мы пишем рантайм абстракцию, мы вынуждены сталкиваться со всеми теми проблемами, которые вы описали. Если мы статически анализируем шаблон и генерируем код в точности на основе кода компонента, то мы избавлены от лишних вычислений в рантайме. Или как выражается Рич Харрис:

Мы компилятор, мы не обязаны играть по общим правилам.

Проще всего это представлять, будто ты сам пишешь код и как бы ты сделал это наиболее простым и прямолинейным способом. Хотим ли мы писать так? Нет, конечно. После нескольких таких компонентов обязательно захочется написать какую-то общую функцию инвалидации/чекинга/reconcile/whatever, чтобы было удобно. То есть создать обобщенный рантайм. Но компилятор не так привередлив, ему не сложно написать такой код за нас.

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

Это замечательно, но анализ шаблонов ноды не обновляет :)
Так что в рантайме светлте в любом случае делает что-то из предложенного.


Нужно ли знать структуру объекта до компиляции, чтобы обновить DOM?

Некорректный вопрос, т.к. мы не можем знать структуру объекта до компиляции. правильно спросить: "надо ли знать структуру объекта?". Для точечных изменений без cd — да, надо. Если вы не обновляете дом, или используете cd или рендерите все целиком — не надо.


Точно знать какие именно поля изменились нам не нужно, так же как чекать все эти поля.

Тогда откуда вы узнаете, какие ноды надо обновлять? Или вы просто обновляете все ноды? Или вы вообще ничего не обновляете?


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


Получили точечное изменение DOM только для соответствующего элемента.

Нет, не получили. Что если в вашем шаблоне obj.baz.baz2 не упоминалось а упоминалось во вложенном шаблоне? Тогда соответствующей проверки в $$invalidate не будет. Где она будет тогда?


Собственно тут даже рассматривать нечего. Все будет тоже самое, только условная функция invalidate для вложенного компонента будет другая. Но принцип работы будет тот же — компонент получает «сигнал», что объект как-то изменился

Ну вот вы прокинули obj.baz внутрь другого компонента, он не менялся, invalidate на нем нет, а obj.baz.baz2 — поменялся. Что дальше?

Это замечательно, но анализ шаблонов ноды не обновляет :)

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

Так что в рантайме светлте в любом случае делает что-то из предложенного.

В рантайме Svelte просто проверят заранее сгенерированные условия и выполняет заранее подготовленые DOM манипуляции. Собственно я вам совершенно конкретно написал что происходит и там нет никаких лишних вычислений.

Некорректный вопрос, т.к. мы не можем знать структуру объекта до компиляции. правильно спросить: «надо ли знать структуру объекта?». Для точечных изменений без cd — да, надо. Если вы не обновляете дом, или используете cd или рендерите все целиком — не надо.

Если под cd вы подразумеваете простую проверку типа:

if (changed.obj && t1_value !== (t1_value = obj.foo))


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

Тогда откуда вы узнаете, какие ноды надо обновлять?

Такое ощущение, что вы уже тролите. Я вам написал конкретный код, прочитайте его пожалуйста. Примерно такой же код генерирует Svelte. Там черным по белому написано какие ноды на какие изменения обновлять.

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

Или вы просто обновляете все ноды?

Нет, svelte не обновляте все ноды.

Или вы вообще ничего не обновляете?

Svelte обновляет только то, что нужно.

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

Я вам написал достаточно примеров, чтобы вы сами ответили на свой вопрос. Так какой из подходов использует Svelte по-вашему и почему вы так думаете?

Нет, не получили. Что если в вашем шаблоне obj.baz.baz2 не упоминалось а упоминалось во вложенном шаблоне? Тогда соответствующей проверки в $$invalidate не будет. Где она будет тогда?

В Svelte нет никаких вложенный шаблонов, только компоненты. Поэтому я вообще не понял о чем вы.

Ну вот вы прокинули obj.baz внутрь другого компонента, он не менялся, invalidate на нем нет, а obj.baz.baz2 — поменялся. Что дальше?

Иногда мне кажется, что вы просто не читаете код, который вам присылают. Именно поэтому наш диалог такой долгий.

Еще раз, Svelte ни каким образом не интересуется общей структурой объекта и не отлавливает изменения в каждой его части.

Итак, попробую представить то, что вы написали в виде примера:

<Component baz={obj.baz} /> <!-- прокинули -->

<script>
  import Component from './Component.svelte';
  
  export let obj = {};
  ...
  obj.baz.baz2 = 10; // поменяли в какой-то момент
  invalidate({ obj: true }); // инвалидируется всегда весь объект
  ...
</script>


Так как какую бы часть объекта мы не поменяли, инвалидируется весь объект. Ну и далее по ходу там примерно такой код (внимание песевдокод):

if (changed.obj) {
  c1.$set({ baz: obj.baz }); // с1 - вложенный компонент
}


Отмечу что это если иммутабильность не включена. Если включена (мы обычно ее включаем), то дальше код ясно дело не пойдет, пока не сменится ссылка на объект obj.baz.

Но даже, без иммутабильности, когда Svelte бездумно инвалидирует абсолютно все объекты и функции, ничего страшного не произойдет и вот почему:

Component.svelte

<p>{baz.baz1}</p>

<script>
   export let baz = {};
</script>


Отгадайте сколько манипуляций в DOM произойдет во вложенном компоненте, если в родительском компоненте поменялось свойство obj.baz.baz2? Правильный ответ — ноль. Сколько вычислений произойдет? Правильно — ноль.
Поэтому я думаю что это не стоит называть cd.
А что вы подразумеваете под «cd»?

Такого cd не было и не может быть в любом другом, не компилируемом, фреймворке
Англяры делают компилляцию на лету, поэтому там тоже самое но в другом виде.
В этом плане не важно в какой момент откомпилировалось, главное что отрабатывает именно скомпилленый кусок.
Насколько я вижу, у вас некоторое недопонимание. По-моему Druu говорит о кейсах типа (уж извините, React-like пседокод):
```
class RootComponent {
state = {
firstName: 'Jhon',
lastName: 'Dow',
whichName: 'firstName'
}

render() {
<select value=state.whichName onChange={e => state.whichName = e.target.value}>

Full name
{state.firstName}
{state.lastName}
{state[state.whichName]}
}
}
```
Ваш оппонент, по-моему о том, что Svelte не сможет определить что на изменение пользователем селекта, нужно менять только две ноды в DOM (select и последний span без глубокого сравнения state либо результатов render
Druu
что Svelte не сможет определить что на изменение пользователем селекта, нужно менять только две ноды в DOM (select и последний span без глубокого сравнения state либо результатов render
В кратце работает так:
1. вы меняете например «obj.user.name», svelte инвалилирует «obj» (корневой объект)
2. на следующем тике проверяются все биндинги которые начинаются с «obj» (т.е. входят в состав этого объекта). т.е. если есть 2 биндинга {obj.user.name} и {obj.user.getAge()}, они оба будут вызваны (высчитаны) и сверятся с предыдущим значением (не все данные в obj, а только все биндинги в obj)
3. если значение изменлось оно сравнивается с тем что лежит в DOM и если оно отличается — то оно обновляется.

Итого: DOM обновляется точечно, весь стейт не проверяется, а проверяются все биндинги у которых инвалидировался корневой объект.

нужно менять только две ноды в DOM (select и последний span без глубокого сравнения state
Для этого примера если данные так и хранить объектом, будет произведено 4 проверки биндингов (или 2 если переменные лежат вне объекта), и изменен только {state[state.whichName]}, а селект не будет изменен т.к. уже имеет актуальное значение.
При это не важно если в стейте ещё 100500 других полей.
Но самое веселье начинается, когда у нас массив из 100500 элементов, каждый из которых передаётся в свой компонент, а потом мы добавляем ещё один элемент в начало — привет, полная ревалидация всех компонент в списке.
Это зависит от реализации, топовые фреймворки как правило это нормально отлавливают и делают 1 вставку.
Мы же тут не про абстрактный топовый фреймворк в вакууме, а про конкретную логику инвалидации.
Анализ шаблонов позволяет заранее вычислять какие ноды нужно обновить для какого изменения.

Не позволяет, это невозможно. На этапе компиляции такой информации просто не существует. Зачем вы пишите вещи, которые заведомо ложны? Не понятно.
На этапе компиляции у вас даже нет информации о том, какие вообще ноды будут в вашем приложении. Как вы собрались анализировать изменения того, о существовании чего не знаете?


В рантайме Svelte просто проверят заранее сгенерированные условия и выполняет заранее подготовленые DOM манипуляции.

Тогда часть изменений будет гарантированно пропущена.


Только для тех полей объекта, которые используются в шаблоне

Ну то есть мы бегаем по всем биндингам в текущем поддереве и проверяем не изменилось ли чего в каждом, так?


Откуда Svelte знает, какие ноды надо обновлять? Уже писал — он анализирует шаблоны и понимает от каких кусков стейта зависят какие ноды и генерирует соответствующий код обновления.

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


В Svelte нет никаких вложенный шаблонов, только компоненты. Поэтому я вообще не понял о чем вы.

Имелся в виду шаблон компонента, который (компонент) используется в шаблоне другого компонента.


Ну и далее по ходу там примерно такой код (внимание песевдокод):

Ну так а дальше-то что происходит? obj.baz не изменилось, откуда свелте узнает, что изменился obj.baz.baz2 и что надо обновить ноду, которая использует соответсвующее значение? Как светлте узнает, что в биндингах вложенного компонента что-то изменилось, когда пропсы остались неизменными?

Ну то есть мы бегаем по всем биндингам в текущем поддереве и проверяем не изменилось ли чего в каждом, так?
Так.
Не позволяет, это невозможно. На этапе компиляции такой информации просто не существует. Зачем вы пишите вещи, которые заведомо ложны? Не понятно.
Похоже вы спортие про разные вещи, приведите пример того «что невозможно», или можете просто посмотреть во что компилируется код (его там совсем не много), тогда все вопросы отпадут.
Похоже вы спортие про разные вещи, приведите пример того «что невозможно»

Узнать структуру дома вашего приложения, например.
Потому что ее, на самом деле, нет — дом со временем меняется. Значит, и биндинги меняются. По-этому нет и не может быть никакого "скомпилированного кода", который что-то там проверяет.

«Видишь суслика? Нет. А он есть!» (с)

Если серьёзно, давайте уже пример. Лично я устал, что с вами общаются на языке кода, а вы лишь концепциями и бла-бла бросаетесь. Есть пример — есть разговор, нет примера, тогда и разговаривать не о чем.
Если серьёзно, давайте уже пример.

Я вам привел конкретный пример. У вас есть obj, в нем есть baz, который прокидывается внутрь вложенного компонента, и в нем есть baz2, который используется в биндинге. Мы изменяем baz2 из внешнег окомпонента, как свелте об этом узнает?
Я уже постов пять пытаюсь у вас выяснить про этот конкретный кейз, а слышу в ответ пространные рассуждения о анализе шаблонов, которые вообще никак не относятся к делу.


Я вам полностью описал как это работает (за исключением шедулинга изменений DOM, но это не принципиальная фича).

А я еще раз вам говорю, что это не работает. Вы просто совсем не то.


Я вам привел пример кода, упрощенный, но близкий к тому, который генериться.

Нет, вы никакого кода не привели, вы привели один единственный вызов, который делает непонятно что. Но вопрос-то в том и состоит что скрывается за этим вызовом doSomeMagic$$.


Извините, но это бред.

Это не бред, это математический факт.


мы спокойно проанализируем его статически с помощью своего же мозга:

Проанализируйте плиз вот такое с помощью своего мозга:


isZfcConsistent() ? <h1>{obj.foo}</h1> : <h2>obj.baz.baz2</h2>

где ifZfcConsistent() ищет контрпример для Zfc. Какие биндинги будут в вашем приложении?


Еще раз, весь шаблон компонента доступен

Шаблон компонента — это не сам дом. Шаблон компонента — это написанная на тьюринг-полном языке программа, которая генерит dom. Вы не можете по эжтой программе определить, какой именно дом она сгенерит. Но даже не в этом дело — у вас в разные моменты вообще РАЗНЫЙ ДОМ И РАЗНЫЕ БИНДИНГИ.
По-этому сам вопрос смысла не имеет. У приложения есть дом в конкретный момент времени при конкретных входных данных, но нет никакого дома "в общем", который вы могли бы вычислить.


Не по всем, а только по тем, которые были инвалидированы в рамках одного тика и только по биндингам, а не по всему стейту.

Ну так еще раз, вы во внешнем компоненте меняется obj.baz.baz2, инвалидируется obj с-но, инвалидируются ВСЕ биндинги в этом поддереве. Так? Или как определяется, какие биндинги инвалидировать при obj.baz.baz2 = ...?


Далее, внутри компонента c1 происходит примерно это:

Ну то есть у вас как в ангуляре бегает cd по всему абсолютно поддереву, что я и пытался изначально выяснить. То есть верный вариант — вариант #2, перебор всех биндингов.
Могли бы сразу об этом сказать а не задвигать странные речуги про компиляцию и прочую дичь.

У вас есть obj, в нем есть baz, который прокидывается внутрь вложенного компонента, и в нем есть baz2, который используется в биндинге. Мы изменяем baz2 из внешнег окомпонента, как свелте об этом узнает?
Вот так, только что проверил, это ключевой кусок из скомпилленого приложения:
var component_changes = {};
if (changed.obj) component_changes.data = ctx.obj.baz;
component.$set(component_changes);
Я вам привел конкретный пример. У вас есть obj, в нем есть baz, который прокидывается внутрь вложенного компонента, и в нем есть baz2, который используется в биндинге.

Я вам решил этот пример, даже более интересный, когда baz2 не используется в биндинге внутри вложенного компонента.

Мы изменяем baz2 из внешнег окомпонента, как свелте об этом узнает?

Об этом вам уже вот тут ответили. Хотя это принципиально и не отличается от псевдо-кода, который я писал вам до этого.

А я еще раз вам говорю, что это не работает. Вы просто совсем не то.

Я просто совсем то, и это работает. Более того, вы даже можете проверить как это работает, если захотите.

Нет, вы никакого кода не привели, вы привели один единственный вызов, который делает непонятно что. Но вопрос-то в том и состоит что скрывается за этим вызовом doSomeMagic$$.

Нет, я вам привел ровно тот код, который позволяет понять что происходит под капотом. Кроме этого кода там есть еще рантайм по микротаскам и шедуленгу изменений, а также десяток хелперов поверх DOM операций. Все.

Это не бред, это математический факт.

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

Проанализируйте плиз вот такое с помощью своего мозга:

С точки зрения статического анализа ничего ровном счетом не поменялось.

Шаблон компонента — это не сам дом. Шаблон компонента — это написанная на тьюринг-полном языке программа, которая генерит dom. Вы не можете по эжтой программе определить, какой именно дом она сгенерит. Но даже не в этом дело — у вас в разные моменты вообще РАЗНЫЙ ДОМ И РАЗНЫЕ БИНДИНГИ.
По-этому сам вопрос смысла не имеет. У приложения есть дом в конкретный момент времени при конкретных входных данных, но нет никакого дома «в общем», который вы могли бы вычислить.

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

Ну так еще раз, вы во внешнем компоненте меняется obj.baz.baz2, инвалидируется obj с-но, инвалидируются ВСЕ биндинги в этом поддереве. Так? Или как определяется, какие биндинги инвалидировать при obj.baz.baz2 = ...?

Вы легко можете дописать пример для тех манипуляций с obj, которые вы хотите сделать и посмотреть что получится. Я устал жевать за вас. Вам уже 3 человека пытаются объяснить одно и тоже.

Ну то есть у вас как в ангуляре бегает cd по всему абсолютно поддереву, что я и пытался изначально выяснить. То есть верный вариант — вариант #2, перебор всех биндингов.

Только он не бегает ни по какому поддереву/скоупу/стейту, как вы написали, а лишь проверяет N заранее сгенерированных строгих сравнений. Реально проверяться будут только биндинги связанные с инвалидированным куском стейта (в данном случае buz для вложенного компонента) и даже если вы в шаблоне вложенного компонента используете хоть 20 свойств из buz, то это всего лишь 20 быстрых, строгих сравнений, большая часть из которых просто не сработает. Поэтому никакой лишней рантайм работы, как в случае с биндингами ангуляра, тут нет.

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

Это не странные речи, потому что в angular нет компиляции, а значит он не может в рантайме работать с N строгих сравнений, которые отрабатывают моментально. Несмотря на то, что звучит это страшно «перебор всех биндингов», на деле, благодаря той самой компиляции, операция эта почти ничего не стоит.
Об этом вам уже вот тут ответили. Хотя это принципиально и не отличается от псевдо-кода, который я писал вам до этого.

Как бы в томи проблема что не отличается, вы за вызовом в псевдокоде как раз и скрываете ту логику, о которой я спрашиваю. Как свелте узнает, что baz2 изменился? В вашем коде ничего про baz2 нет.


Нет, я вам привел ровно тот код, который позволяет понять что происходит под капотом.

Я спрашиваю вас, "что делает код х", и вы мне в ответ приводите сам код х. Где тут логика?


С точки зрения статического анализа ничего ровном счетом не поменялось.

Что не поменялось? У вас свелте решил алгоритмически неразрешимую задачу? А можно увидеть ответ?


Я устал жевать за вас.

Я уже устал вам объяснять, что вы отвечаете на какой-то другой вопрос (я даже не знаю накакой, если честно), который я не задавал. Я вас спрашиваю, откуда свелте узнает об изменении baz2. Вы в ответ пишите:


c1.$set({ baz: obj.baz })

Отлично, ну а дальше-то что? что именно происходит в set и после его вызова? Как этот вызов приводит к тому, что свелте узнает обь изменении baz2? Ведь в этом коде нету ничего про baz2. В этом состоит вопрос.


Учитывая, что REPL доступен и можно прочитать весь код вашего примера за пару минут

Эм, там нету кода решения задачи ifZfcConsistent, там вот так:


if (isZfcConsistent()) return create_if_block;

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


Только он не бегает ни по какому поддереву/скоупу/стейту, как вы написали, а лишь проверяет N заранее сгенерированных строгих сравнений.

А если у вас список на тысячу элементов? Генерируете 1000 сравнений? А если неизвестно сколько там элементов в компайлтайме, то что тогда, сколько сравнений генерируется?

Как бы в томи проблема что не отличается, вы за вызовом в псевдокоде как раз и скрываете ту логику, о которой я спрашиваю. Как свелте узнает, что baz2 изменился? В вашем коде ничего про baz2 нет.

Где вы тут увидели вызов за которым я могу что-то спрятать:

if (changed.obj && t1_value !== (t1_value = obj.foo))

Или у вас просто не получается перенести понимания этого кода в подкомпонент c1, который я привожу в другом примере:

if (changed.obj) {
  c1.$set({ baz: obj.baz }); // с1 - вложенный компонент
}

Хорошо, я сделаю это за вас:

if (changed.baz && t1_value !== (t1_value = obj.baz.baz2) {
  t1.data = t1_value;
}

Очень надеюсь что вы догадались что последний пример находится внутри c1, а предпоследний внутри parent-компонента.

Я спрашиваю вас, «что делает код х», и вы мне в ответ приводите сам код х. Где тут логика?

Логика очень простая — я думал что вы умеете читать код. Кажется я ошибся.

Что не поменялось? У вас свелте решил алгоритмически неразрешимую задачу? А можно увидеть ответ?

Svelte не решает эту задачу. Его дело вызвать функцию и получить в итоге результат. В зависимости от результата отрисовать один из вариантов разметки. Все.

Я уже устал вам объяснять, что вы отвечаете на какой-то другой вопрос (я даже не знаю накакой, если честно), который я не задавал. Я вас спрашиваю, откуда свелте узнает об изменении baz2. Вы в ответ пишите:

На этот вопрос я уже отвечал много раз и не только я:
obj.baz.baz2 = 3;
$$invalidate('obj'); // вот отсюда он узнал


Отлично, ну а дальше-то что? что именно происходит в set и после его вызова? Как этот вызов приводит к тому, что свелте узнает обь изменении baz2? Ведь в этом коде нету ничего про baz2. В этом состоит вопрос.

Писал выше в этом же комментарии полное решение, но не уверен, что вы уловили сразу, поэтому дублирую:

if (changed.baz && t1_value !== (t1_value = obj.baz.baz2) {
  t1.data = t1_value;
}

Еще раз, это внутри c1. Последовательность действий:

1) parent компонент выставил измененный стейт в c1 через метод $set у c1. Внутри $set делается $$invalidate('baz') для с1.

2) c1 проверил изменилось ли значение baz2, если да, то обновил DOM, если нет, то ничего не сделал.

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

Чего? Кто вам говорил, что он не вызовет функцию, если написан вызов функции. Конечно он ее вызовет и в зависимости от результата отрисует тот или иной блок. Вы прям уже бред начали писать.

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

Конечно знает. Он занает, что если эта фунция вернула true, тогда элементу h1 выставляем значение obj.foo, наоборот выставляем элементу h2 значение obj.baz.baz2. То есть он точно знает какой стейт соответствует какой DOM операции. Например React этого не знает, он лишь может узнать отражение этой операции на дубликате vdom.

А если у вас список на тысячу элементов? Генерируете 1000 сравнений? А если неизвестно сколько там элементов в компайлтайме, то что тогда, сколько сравнений генерируется?

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

Еще раз отмечу, что вы в дейтвительности не хотите разобраться, потому что если бы хотели, вам в разы было бы проще самому прочитать 100 строк кода, чем вступать в демагогию основанную на «воде», которую вы изрядно льете в своих комментариях.

Писал выше в этом же комментарии полное решение, но не уверен, что вы уловили сразу, поэтому дублирую:

Ну это обычный change detection, как в ангуляре. При чем тут статический анализ и прочие бредни? Просто обходится все дерево и проверяются все значения.


Я с самого начала об этом и говорил, но вы зачем-то выдумываете, что "все не так и свелте что-то магически знает, хотя знать не может". Нет, свелте ничего не знает (логично, раз не может). Он просто обходит весь дом целиком и сравнивает все значения, как я и сказал вначале. И именно это происходит в приведенном вами коде, вопрос закрыт.

Просто обходится все дерево и проверяются все значения.

Не все дерево, а несколько заранее приготовленных строгий стравнений. Покажите мне где в ангуляре заранее генерируются условия для всех кейсов в buildtime? Разве что речь про последний ангуляр с AoT, так это оно и есть примерно.

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

Да ежкин код. Он НЕ обходит ВЕСЬ DOM и даже ВЕСЬ стейт целиком. Он обходит только то, что нужно обойти, это и значит «он знает». Вы тугой как пробка.

Хорошо, давайте разберем конкретный кейс и сравним Реакт и Svelte. Вот есть у нас компонент:

<div class="foo">
  <h1>Hello world</h1>
  <p class="text">Lorem ipsum</p>
  <form action="/login" method="post">
    <input type="email" placeholder="Enter email">
    <input type="password" placeholder="Enter password">
    <button>Login</button>
    <button type="reset">Clear</button>
  </form>
</div>


И в какой-то момент времени изменяется стейт:

// react
this.setState({
   email: 'example@mail.com'
});

//svelte
email = 'example@mail.com';

Итак, сколько операций cd (или reconcile в случае с vdom) произойдет в этом случае?

Сколько сделает React — более 20ти по грубым подсчетам и это только реконсиляция (без самого ререндера дерева)
А сколько нужно? Ноль. Сколько сделает Svelte — ноль.

Ок, пошли дальше. Добавим немного динамики:

...
<input value={email} type="email" placeholder="Enter email">
...

React — все еще более 20-ти. Сколько нужно? Одну. Сколько сделает Svelte — одну.

Сколько манипуляций в DOM — одна. В случае с React операция в реальный DOM тоже будет одна. Только, насколько я понимаю, будет также выполнена операция получения реального DOM элемента (querySelector), а в Svelte просто:

if (changed.email) {
  input0.value = ctx.email;
}

input0 — это уже закэшированное значение нужного элемента.

Чувствуете разницу? 20 к 0 при ноле необходимых и 20 к 1 при одной необходимой. По вашему это все одно и тоже?
Не позволяет, это невозможно. На этапе компиляции такой информации просто не существует. Зачем вы пишите вещи, которые заведомо ложны? Не понятно.

Я вам полностью описал как это работает (за исключением шедулинга изменений DOM, но это не принципиальная фича). Я вам привел пример кода, упрощенный, но близкий к тому, который генериться. REPL позволяет посмотреть итоговый код для каждого изменения компонента. Если бы вы захотели разобраться, то давно бы уже сделали это. Поэтому ненужно меня обвинять в ложных заявлениях.

На этапе компиляции у вас даже нет информации о том, какие вообще ноды будут в вашем приложении.

Извините, но это бред. Компонент работает только с тем куском DOM, который представлен у него в шаблоне и если мы возьмем пример выше, мы спокойно проанализируем его статически с помощью своего же мозга:

<h1>{obj.foo}</h1>
<h2>{obj.baz.baz2}</h2>

то очевидно что у нас есть 2 элемента (h1 и h2), текстовое содержимое которых связано с двумя кусками стейта (obj.foo и obj.baz.baz2). При изменении obj.foo нужно обновить текст элемента h1, а при измении obj.baz.baz2 нужно изменить текст h2. Вот простейший статический анализ шаблона.

Как вы собрались анализировать изменения того, о существовании чего не знаете?

Еще раз, весь шаблон компонента доступен, там нет никаких незвестных для компонента частей, даже если это условия или циклы:

{#if obj.foo < 10}
<h1>{obj.foo}</h1>
{/if}

С точки зрения статического анализа ничего не поменялось.

Тогда часть изменений будет гарантированно пропущена.

Каким образом? Если в шаблоне не используется obj.qux и оно не является зависимостью в каком-то reactive declaration, то зачем вообще отслеживать это изменение?

Ну то есть мы бегаем по всем биндингам в текущем поддереве и проверяем не изменилось ли чего в каждом, так?

Не по всем, а только по тем, которые были инвалидированы в рамках одного тика и только по биндингам, а не по всему стейту. Условия для проверки каждого биндинга генерируются настолько конкретно и просто, что сама операция проверки обычно сводится в парочке строгих стравнений, что само по себе почти бесплатно. Если проверка сработала, то операция в DOM генерируется так же очень конкретным и оптимальным образом для каждого изменения. Поэтому в этой части что-то оптимизировать тоже сложно.

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

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

Имелся в виду шаблон компонента, который (компонент) используется в шаблоне другого компонента.

Компоненты полностью изолированы друг от друга, поэтому компонент, в котором используется другой компонент ничего не знает о его шаблоне и о DOM за который тот отвечает. Биндинги и их проверки генерируются для каждого компонента в отдельности и работают независимо. Кроме того, несмотря на то, что все компонента находятся в рамках одного дерева DOM, так как внутри не происходит никакого querying'а по DOM и все элементы, участвующие в шаблоне заранее закэшированы, работа ведется только с ними.

Ну так а дальше-то что происходит? obj.baz не изменилось, откуда свелте узнает, что изменился obj.baz.baz2 и что надо обновить ноду, которая использует соответсвующее значение? Как светлте узнает, что в биндингах вложенного компонента что-то изменилось, когда пропсы остались неизменными?

Ох, да ежкин код! Вы не можете перенести знания из других примеров во вложенный компонент? Ладно, давайте уточним:

c1.$set({ baz: obj.baz });

В этот момент вложенный компонент c1 получает «сигнал» проперти baz вероятно изменился. Если мы работает в режиме БЕЗ иммутабильности, то это сработает даже если ссылка на объект не поменялась, если с иммутабильностью, то ясно дело, нужно сперва создать новый объект. Но это мелочи.

Далее, внутри компонента c1 происходит примерно это:

if (changed.baz && t1_value !== (t1_value = baz.baz1)) {
     t1.data = t1_value;
}

Так как значение baz.baz1 не поменялось (напоминаю мы меняли значение baz.baz2), то никаких DOM манипуляций не будет произведено.
переменная могла измениться. Вы никак не можете заранее скзаать какая — могла любая.
Если какое-то изменение нельзя отловить «в лоб» на уровне компиляции, то Svelte его просто «не видит». Для этого нужно обкладывать computed и пр. махинации (второй пример)

вы просто перерендериваете весь дом для bar целиком — подход ангуляра
Ангуляр как раз делает обновления точечно, т.е. ваш 2-й вариант:
2. вы перебираете поочередно все биндинги и смотрите, какие из них изменились, делая точечные апдейты

— подход реакта
А вот реакт наоборот, генерирует VDOM целиком и потом уже ищет что в нем изменилось (т.е. такой dirty-checking только по VDOM), чтобы сделать минимальные изменения в DOM.
Т.е. ангуляр ищет изменения в данных, чтобы сделать точечный апдейт, а реакт ищет изменения в VDOM для точечного обновления.
А вот реакт наоборот

Да, я был невнимателен и перепутал пункты, когда дописывал пост :)
Все наоборот, конечно.


Если какое-то изменение нельзя отловить «в лоб» на уровне компиляции, то Svelte его просто «не видит».

Понятно. Ну это такое себе.


Т.е. ангуляр ищет изменения в данных, чтобы сделать точечный апдейт

Вообще, насколько я помню, точечность ангуляра идет в рамках компонента. То есть, если какой-то биндинг инвалидирован — перерисовываются все ноды компонента (именно самого компонента, для вложенных компонент уже отдельный cd и перерисовка). В иви по крайней мере точно будет так, т.к. там генерируется цельная ф-я апдейта.

Даже set_data() абсолютно дешевый:

export function set_data(text, data) {
	data = '' + data;
	if (text.data !== data) text.data = data;
}

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

отмечаем основные моменты
«Основные моменты» игнорируют упомянутые проблемы, что не компенсируется восторженными эмоциональными фразами.

не знаю что может быть проще и эффективнее
не считаю проблемой
Ок, в данной части ваша позиция ясна. Можно было сэкономить кучу времени, если не выдумывать про бандлер, эджкейсы и т. п.

опцию immutable: true (которую кстати мы всегда используем)
В таком случае информация про опцию, которую "мы всегда используем" вместо некорректного дефолтного поведения, и необходимость копировать объект при каждом изменении, должна была находиться в шапке каждого поста. А если псевдо-иммутабельность нужна всегда, то…

реальные проекты
Столько слов о мифических «реальных проектах» не сопровождены ссылками. Вдруг они великолепно покажут, насколько хорош именно svelte. Или в них такие же дыры и они покажут обратное.

Коммерческая деятельность сама по себе не аргумент, в растущем сегменте рынка мы все гении, не надо путать факт деятельности с ее качеством.

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

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

Ну так, свою реализацию в студию. Ну или хотя бы что-то чужое, что вы считаете рабочим. Посмотрим, разберем по полочкам. К сожалению, но умничать со стороны всегда проще.
Ловлю на слове. Так и быть, вот один из вариантов в строке: ''. Не идеальный, но вполне рабочий. Сколько времени понадобится на разбор по полочкам популярного интерпретатора/компилятора JS на выбор? Где косяки в алгоритмах и генерируемом коде? С удовольствием предоставляю вам простую возможность поумничать со стороны.
Мне еще веселее, и заказчиков можем пригласить посмеяться, будет закономерно, если им захочется сделать аудит и получить компенсацию. Для экономии времени, опущу многие очевидно неверные вещи вроде неправильного типа события, утверждений про максимальную простоту и эффективность, вставку функции куда попало и прочее.

Вы что ли будете аудит проводить? Ха-ха. Очередной хабротроль. Не утомляйте людей своим набором буллшита. Сомневаюсь что у вас вообще есть заказчики и кстати, раз вы такой крутой, может поделитесь чем вы занимаетесь? Если нет, тогда об чем речь?

«Основные моменты» игнорируют упомянутые проблемы, что не компенсируется восторженными эмоциональными фразами.

Основные моменты вы даже не смогли видимо осилить. По поводу озвученных вами проблем, повторю еще раз: 1) проблемы с name нет, так работает js, но при этом компилятор предупреждает о потенциальных проблемах. 2) По-поводу не нулевого прототипа, нужно еще проверять, действительно ли это имеет проблемы в данном контексте или вы просто троль и сказочник.

В таком случае информация про опцию, которую «мы всегда используем» вместо некорректного дефолтного поведения, и необходимость копировать объект при каждом изменении, должна была находиться в шапке каждого поста. А если псевдо-иммутабельность нужна всегда, то…

Это нам она нужна всегда, а новичкам нет, потому что значительно сложнее объяснить как применить изменения к DOM если включена иммутабильность. С точки зрения всего остального, новички разницы не заметят.

Столько слов о мифических «реальных проектах» не сопровождены ссылками. Вдруг они великолепно покажут, насколько хорош именно svelte. Или в них такие же дыры и они покажут обратное.

Коммерческая деятельность сама по себе не аргумент, в растущем сегменте рынка мы все гении, не надо путать факт деятельности с ее качеством.

Компания существует с 2013 года (можете в whois слазить), без реальных проектов просуществовать больше 5 лет невозможно. О коммерческих проектах не всегда можно рассказывать публично, да и толку это делать вам, тролю. От вас то мы даже на open-source проекты ссылок не дождемся, потому что их нет.

То есть вместо понятного разработчикам языка, надо было начать ветку следующим комментарием: «вот реальный (тм) серьезный (тм) проект, у него больше уников, чем у всех ваших svelte-проектов в сумме, поэтому всем делать, как я сказал». И вы бы такой: «человек выглядит авторитетным с реальными проектами, пойду выполнять указания». Такая адекватность мне не близка.

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

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

Не просто отвлеченно, но и бездумно. Зато потом делается глобальный вывод: Очень сырой фреймворк.. Может вы и любитель голословных заявлений, но я нет. Не хотите пробовать Svelte, потому что считаете его сырым — дело ваше, но тогда не утверждайте что это так, пока не попробуете и не столкнетесь с реальными, а главное фундаментальными его проблемами (мелкие баги и недоработки бывают везде). Тогда ждем от вас реальную статью, с реальными примерами и описанием реальных проблем. Я первый ее твитну, обещаю вам.

Ловлю на слове. Так и быть, вот один из вариантов в строке: ''. Не идеальный, но вполне рабочий.

Все что нужно знать о вас — пустая строка.

С удовольствием предоставляю вам простую возможность поумничать со стороны.

А кстати, вы приходите на HolyJS в мае, я меня как раз там будет 2-х часовой воркшоп по Svelte. Там и пообщаемся предметно. Хотя вряд ли, там билет дорогой, а у студента таких денег нет наверное. Жаль.

Про цикл наброс был на то, что раз уж у вас умный компилятор
Зачем делать for(i...) { ++i; $$invalidate('i', i) }
Когда можно
for(i...) ++i;
$$invalidate('i', i)

Хотя если invalidate дешевый я тоже не понимаю, отчего ecmaeology так всполошился
Ему просто делать нечего между парами. ;-)

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

Все зависит от того, как вы это напишете. Если например так:

let i = 0;
for(; i < 5; i++) {
  console.log(i);
}

Он вам сгенерирует код таким образом:
let i = 0;
for(; i < 5; i++) {
  console.log(i); $$invalidate('i', i);
}

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

А если по-другому:
let i = 0;
for(; i < 5; i++) {
}
console.log(i);

то и код будет другой:
let i = 0;
for(; i < 5; i++) {
} $$invalidate('i', i);
console.log(i);


Компилятор конечно не идеален, но и вы не слушайте всяких… И опять же сам $$invalidate почти ничего не стоит.
Восхитительная штука! Долго ждал шанса попробовать, и решился для внутреннего инструмента — ползунки, элементы, динамические данные. Особенно контраст заметен при переписывании с vanilla (прототип на нём был сделан) — больше половины кода выкинул.

Из плюсов:
  • Низкий порог вхождения, коллеги сказали что можно сразу понять как это всё работает, и новичок в отрасли может за 1 день вкурить учебник (я сам не из фронтенда). Учебник кстати очень классный, и на русском языке ru.svelte.dev/tutorial/basics
  • Модульность и CSS инкапсуляция по-моему одна из лучших
  • Быстрый и ванильный
  • Легкий и независимый
  • Меньше кода в сравнении с аналогами
  • Анимации


Считаю кандидатом #1 в качестве киллера React и VueJS в будущем.
А что там насчет отложенного рендеринга? Ну то есть у меня приходят данные быстрее, чем браузер способен переварить, и мне надо как-то решать динамически, что отрисовывать, а что нет. Аналог shouldComponentUpdate есть?
Думаю вам сначала будет интересно посмотреть видео из статьи, в котором автор Svelte вполне недвусмысленно, фактически, уже ответил на все ваши вопросы. Если появятся новые, то можем продолжить обсуждение.

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

У нас очень дружественное комьюнити. Добавляйтесь в телеграм чат, если сомневаетесь!

Мне кажется дружественно будет, прежде чем размещать вопросы к публикации, сперва ознакомиться с материалами публикации, включая видео-материалы, если они являются необъямлемой частью публикации. Тем более повторение одних и тех же вопросов, на которые люди, посмотревшие видео, уже получили ответы.

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

Когда данные обновляются слишком часто, лишние перерисовки могут приводить к проблемам с перфомансом на сложных страницах (как правило из-за нешустрого обмена данными DOM <-> JS или layout trashing). Универсального решения для общего случая я пока не встречал. Есть различные компромиссы. Многие фреймворки предоставляют интерфейсы для решения проблемы вручную. Еще одним вариантом служит VDOM с реиспользованием существующей структуры DOM при обновлениях.

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

Дружественно будет подавать материалы в разных видах :) Я сейчас даже не про людей с особыми потребностями.

Наоборот, даже люди с особыми потребностями смогут изучить данный материал!!! Потому что не смотря на то, что Хабр не очень уважает A11y (всего 60 баллов по Lighthouse), зато видео на YouTube очень даже помогает — там есть как звук для слабовидящих и субтитры для слабослышащих.
Я не про них. Лично для меня — видео худший формат подачи знаний в подавляющем большинстве случаев.

Многие люди плохо воспринимают видео-"документацию" в принципе (я проводил эксперименты над собой: больше трёх раз разница для родного языка в знакомом контексте), а многие часто не могут найти комфортных условий, чтоб видео посмотреть.

Данная публикация не является ни видео-«документацией», ни вообще документацией. Это лишь анонс выхода Svelte 3 и приуроченное к этому выступление автора. Более того, это перевод. В конце публикации заботливо собраны все необходимые ссылки для тех, кто плохо воспринимает видео формат.

Кроме того, российское комьюнити и конкретно AlexxNB приложили очень много усилий для того, чтобы перевод документации и туториалов на русский язык был первым среди локализаций (в данный момент существует только английская и русская версии). Надеюсь для вас это окажется максимально комфортным!

Хотел написать статью о своем опыте со Svelte и о разнице с Vue, но новая версия отличается настолько, что уже смысла нет. Вручную обновлять не хочется, а утилита для апгрейда еще не вышла.

Заходите к нам в Телегу. Там у ребят уже есть опыт переезда на Svelte 3 с помощью svelte-upgrade (она где-то там на бранче лежит). Вроде как 2/3 кода автоматом получилось сконвертить. Присоединяйтесь, ребята поделятся опытом!
Кажется я знаю чем буду заниматься на майских)

Больше всего понравилась инкапсуляция CSS, правда не понятно как можно компонент обернуть в другой стиль. Или все же можно обращаться через:

ComponentName { background-color: red }


Однозначный плюс в копилку — интерфейс компонентов, а то в другой «реактивной» библиотеке надоело бить линейкой по рукам.

Увы, так обращаться нельзя. <ComponentName class="some-class"> (как во Vue) тоже нельзя.
Можно делать глобальные стили. Или пробрасывать какие-то пропсы, на основе которых ComponentName сам себе поставит какой-то класс.

правда не понятно как можно компонент обернуть в другой стиль. Или все же можно обращаться через:

Дело в том, что Svelte компоненты не имеют явного рут-элемента, то есть конструкция /> фактически не имеет смысла сама по себе, так как не понятно к чему должен быть применен class.

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

Прошёл учебник (он уже на 3-й версии?), но не понял как писать SPA приложения, прежде всего где размещать и как работать с роутингом и независимыми сервисами на ваниле. Есть где почитать? Sapper посмотрел, но это совсем не то.

Прошёл учебник (он уже на 3-й версии?)

Да

но не понял как писать SPA приложения,

Это учебник по Svelte, а не по написанию SPA. Svelte — это лишь view слой для вашего SPA, UI либа, как React, например.

как работать с роутингом

Svelte не навязывает вам каким образом организовывать роутинг и каким роутером пользоваться. Пример простейшего роутинга с использованием pagejs:

App.svelte
<Navbar {...ctx} />

<svelte:component this={Page} {...ctx} />

<Footer />

<script>
  import Navbar from './components/Navbar.svelte';
  import Footer from './components/Footer.svelte';

  export let Page = null,
    ctx = null;
</script>


main.js
import page from 'page';
import App from './App.svelte';

const app = new App({
  target: document.body
});

page('/posts', ctx => {
  import('./pages/Posts.svelte')
    .then(({ default: Page })=> {
      app.$set({ Page, ctx });
    });
});

page('/', ctx => {
  import('./pages/Home.svelte')
    .then(({ default: Page })=> {
      app.$set({ Page, ctx });
    });
});

page.start();

export default app;


независимыми сервисами на ваниле

Если вопрос о программных сервисах, тогда просто импортируем что нужно в компоненты и используем. Если речь про какие-то ванильные либы, которые также взаимодействуют с DOM, то для этого есть actions — фактически это life-cycle для любого DOM элемента.

Sapper посмотрел, но это совсем не то.

Можете уточнить почему не то? Sapper — это налог Next/Nuxt, то есть application framework для создания универсальных (изоморфных) PWA.

Спасибо, немного понятней стало.


Речь больше о возможности прокинуть в компонент какой-то сервис контейнер или типа того. В идеале неявно заполнять пропсы, типа DI
Может я неправильно понял, но сложилось впечатление, что имеющийся бэкенд с API так не прикрутить, использование сервера из фреймворка обязательно. То есть переход с того же ангуляра сбэком на. PHP без сильных изменений бэка не сделать.

Как таковой копцепции DI в Svelte нет. Пожалуй, реально она есть только в Angular. Думаю можно использовать какие-то сторонние решения для этого. Для обмена стейтом между компонентами в Svelte 3 есть встроенные сторы на observables очень приятным синтаксическим сахаром поверх на основе все той же компиляции. Кроме того, сейчас обсуждает возможность немного расширить поддержку для RxJS, чтобы можно было использовать этот «сахар» и с ним.

Может я неправильно понял, но сложилось впечатление, что имеющийся бэкенд с API так не прикрутить, использование сервера из фреймворка обязательно.

Возможно вы не очень знакомы с изоморфными веб-приложениями, но там обычно используется 3-х звенная архитектура с frontend-севрером на NodeJS и backend-сервером на чем угодно. То есть ваш существующий бэк на PHP вообще можно не трогать, если там уже есть REST API. Немного не скромно, но могу посоветовать свой доклад по этому поводу с FrontendConf, либо вот выпуск RadioJS#55. Там тоже эта тема затрагивалась.
Ага, про сторы в учебнике есть. Больше всего напомнили даже не сторы MobX, а MST поверх него.

Ну я делал приложения с фронтом на реакте с бэкендом на PHP, который обращался к node.js рендер-серверу, когда это было нужно (при первом заходе на страницу), подготавливая все данные для рендеринга заранее. Это показывало лучшее результаты чем направление запрсов на nodejs с дерганием с него методов API.

За ссылки спасибо, посмотрю.
Ну я делал приложения с фронтом на реакте с бэкендом на PHP, который обращался к node.js рендер-серверу,

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

В целом, Sapper — это конечно когда нода стоит фронтом, но и схему, когда нода сбоку, можно легко реализовать на Svelte.
А что с писанием кода на TypeScript? (Поддержка tsx) пробежался бегло по Гуглу, пока очень мало реальных примеров.

Там не tsx в шаблонах.
Автор фреймворка сам любит TS, но поддержки пока нет.

Услышал. Спасибо! Тогда пока оставим это все)

Может, PaulMaly расскажет подробнее о планах на TS.


Но, если честно, если компоненты маленькие и глупые (а они такими и должны быть), то TypeScript там ни к чему. Бизнес-логику можно и нужно выносить в отдельные файлы, и они уже могут быть написаны на TS.

Поддержка TS планируется на уровне компилятора, как и написано в статье. Хотя в данный момент Svelte — это прежде всего стандарт, то есть ES.

Поддержки в шаблонах нет, прежде всего потому что сам TS не поддерживает подобную кастомизацию. Даже Angular, который ставит на TS все, имеет проблемы с шаблонами, насколко мне известно.

В скрипте можно уже сейчас использовать TS с помощью препроцессора: github.com/PaulMaly/svelte-ts-preprocess

Какие-нибудь редакторы уже поддерживают?

TS в *.svelte:) Я пробовал плагины для *.svelte в WebStorm и в VS Code, и там и там грустно с шаблонами.
Есть плагин для VS Code и Webstorm вроде тоже, но там еще есть проблемы с Svelte 3. Он вот только вышел, а все эту штуки чисто community-driven.

Так я про эти плагины и сказал. Там всё плохо.

Svelte 3 вышел меньше недели назад. Это же не company-driven проект, а чистый community. Пока ждем, постим ишаки и PR-ы))
постим ишаки
кстати на мелкую багу сегодня натыкался, что-то типа:
$: a = changed
$: c = a + b
$: b = a + 1
так работает,

$: c = a + b
$: a = changed
$: b = a + 1
а так нет
Интересная, а можете REPL дать, либо есть вам не трудно, завести ишак тут?
Логика отображения и ввода данных может быть достаточно сложной с одной стороны, с другой — имеющей смысл только в контексте конкретного компонента, например, автозаполнение инпутов.
Бывает да, но пользовательский ввод находится в райтайме, а никакого TS и его тайпчекинга там и в помине нет. Для ввода неплохо работают и простые ассерты.

Короче вопрос спорный, лично я за то, чтобы быть ближе к платформе, насколько это возможно, сохраняя при этом нормальный DX. TS это пока не стандарт, поэтому не очень понимаю, почему практически любая статья по JS обязательно венчается обсуждением TS.

Мне кажется, если в тегах к статье typescript не указан и статья не подразумевает его обсуждение, то и холиварить на эту тему не имеет смысла. А уверенность некоторый коллег в том, что TS нужен всем, меня тоже весьма смущает.
Сейчас заглянул в «исходник», $: «computed» переменные не являются какими-то observable объектами — это простые переменные (которые «решаются» на уровне компиляции) — и это жирный плюс в отличие от knockout/vue и подобных.
Да, это чисто магия компилятора и еще присловутый topological order о котором Рич говорил тут. Observables в Svelte только сторы и то с приятным синтаксическим сахаром на основе опять же компляции, который делает их использование более синхронным:

<script>
  import { stars } from './stores.js';
</script>

<p>Github stars: {$stars}</p>

Что-то я чем больше смотрю, тем меньше мне нравится v3:( С одной стороны, конечно, теперь нужно куда меньше отступов и фигурных скобочек, а с другой...


  • <svelte:options option={value}> это уродливо, особенно если мне всего-то надо namespace: 'svg' (интересно, кстати, почему это не детектится в compile-time).
  • Редакторы ругаются на необъявленные переменные, если юзать сторы. Объявить такие переменные документация мешает.
    const count = writable(0); 
    console.log($count);
  • Специальная метка для обозначения реактивности это вообще странно. Никакое обоснование, никакой ментальный выверт, объясняющий почему и зачем, я этому придумать не могу.

В результате в компонентах получается очень странный и неидиоматичный (хотя формально корректный) джаваскрипт.
Есть ли шансы на продолжение развития v2?

<svelte:options option={value}> это уродливо, особенно если мне всего-то надо namespace: 'svg' (интересно, кстати, почему это не детектится в compile-time).

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

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

Редакторы ругаются на необъявленные переменные, если юзать сторы. Объявить такие переменные документация мешает.

Редакторы и на JSX ругаются, если не использовать никаких плагинов. Не думаю что это проблема, нужно просто быстрее внедрить поддержку Svelte 3 в инструменты разработчика. Обычное дело.

Специальная метка для обозначения реактивности это вообще странно. Никакое обоснование, никакой ментальный выверт, объясняющий почему и зачем, я этому придумать не могу.

Вам и ненадо придумывать обоснование, его уже придумал сам автор. Признаюсь меня сперва это тоже немного смущало, однако когда начал писать, это оказалось настолько удобным и лаконичным, что любые субъективные «правильно-неправильно» отошли на второй план. Не зря во время демонстрации именно этой фичи люди в зале даже зааплодировали.

В результате в компонентах получается очень странный и неидиоматичный (хотя формально корректный) джаваскрипт.
Есть ли шансы на продолжение развития v2?

Получается код, который синтаксически является Javascript'ом, но семантически является SvelteScript'ом, которы потом компилируется в Javascript.

Шансов на развитие Svelte 2 думаю нет и я бы лично не хотел этого. С 2013 года пишем на Ractive-like синтаксисе, сперва на самом Ractive, потому на Vue, а потом и на Svelte. Svelte 3 как глоток свежего воздуха.

Я написал очень длинный развернутый ответ, но в итоге решил сэкономить вам время и ограничиться простым "Очень жаль".

Всем не угодишь)) Лично я не вижу смысла компилятору иметь такой же DX, что и обычный рантайм фреймворк. Vue имеет DX, как у Svelte 2. Svelte 3 полностью аутентичен наконец.

Ну нету щас DX, нету его, вы его убили.


Для меня ценность Svelte 2 была в минимальном рантайме и возможности эмбеда, при знакомой парадигме и разумных компромиссах. Теперь мне не на чём писать внедряемые виджеты, разве что городить велосипед на каком-нибудь mobx + lithtml.

Жаль, что вы решили не публиковать свой развернутый ответ, потому что заявления «нету щас DX, нету его, вы его убили», по моему мнению, требуют каких-то пояснений, кроме имхо и вкусовщины.

Вы на полном серьезе считаете, что вот этот DX:

<Nested {bar} />

<script>
  import Nested from './Nested.html';

  export default {
    components: { Nested },
    data() {
      return {
        foo: 1
      };
    },
    computed: {
      bar: ({ foo }) => foo + 1
    }
  };
</script>


Лучше чем:

<Nested {bar} />

<script>
  import Nested from './Nested.svelte';

  export let foo = 1;

  $: bar = foo + 1;
</script>


Ну даже незнаю… У меня на Svelte 2, как и на Vue, без сниппетов в IDE писать просто не получалось. Пока понапишешь все эти скобки и объекты.

Кстати, по повому опций компилятора, лично мое предложение было использовать дефолтный экспорт как и раньше:

<Nested {bar} />

<script>
  import Nested from './Nested.svelte';

  export let foo = 1;

  $: bar = foo + 1;

  export default { 
    namespace: 'svg' 
  };
</script>


Но поддержки мое предложение не нашло, к сожалению.

Если говорить предметно, отбросив предпочтения, то функционал тех же reactive declarations шире чем computed props из Svelte 2:

<script>
  export let foo = 1;

  $: bar = foo + 1; // могут быть не только переменной
  $: console.log({ bar }); // но и фактически любым js выражением
  $: { // даже блочным
     if (bar > 2) {
        alert('Hello world');
     }
  }
</script>


Если вы пишете на Vue, то нет сомнений в том, что Svelte 2 вам ближе по синтаксису. Но Svelte не хочет быть просто Preact'ом, только для Vue. Svelte пытается найти свой аутентичный, неповторимый стиль и DX, который действительно сложно воспроизвести без компиляции и который реально позволяет писать код быстрее и эффективнее.

Думаю амбиции Svelte сделать из «Большой тройки» «Фантастическую четверку», а это вряд ли получится, если все вокруг будут думать, что «это как Vue, только компилируемый».

Ну так вы тоже мне никаких доводов, кроме имхо и вкусовщины, не предоставили:) "Глоток свежего воздуха" — это сугубая вкусовщина:)


Без поддержки редакторов все эти нововведения и неповторимости только доставляют боль. А поддержки этой нет и когда она будет — неизвестно. А если каждую версию ломать синтаксис на ни с чем не совместимый, так можно и вовсе никогда её не дождаться. Svelte 2, кроме шаблонов, терпимо писалась в любом IDE.
Да, я серьезно считаю, что первый DX лучше, потому что редакторы на нём не дохнут. Если бы многословность была действительно проблемой, люди бы не писали на Angular и на, скажем, Java.


функционал тех же reactive declarations шире чем computed props
Ну для императивных вызовов в ответ на обновление стейта был lifecycle hook — назывался, если не ошибаюсь, onupdate.

Я не хотел отправлять развёрнутый ответ, потому что мы друг друга не уговорим. Вам всё нравится, а меня не переубедить, пока не будут рабочие плагины для VS Code или WebStorm, умеющие Svelte 3. Вот когда будут, тогда дам второй шанс.

«Глоток свежего воздуха» — это сугубая вкусовщина:)

Здесь я использую это выражение исключительно в том смысле, что такого синтаксиса и простоты использования мы не видели до этого ни в одном фреймворке. В этом смысле он действительно «свеж», без всякой вкусовщины, но «свеж» совершенно не значит «понравится всем».

Без поддержки редакторов все эти нововведения и неповторимости только доставляют боль. А поддержки этой нет и когда она будет — неизвестно.

То есть ваша основная претензия в поддержке IDE? Ну комон, новая версия вышла только неделю назад, будьте более снизходительным и терпиливым!

А если каждую версию ломать синтаксис на ни с чем не совместимый, так можно и вовсе никогда её не дождаться. Svelte 2, кроме шаблонов, терпимо писалась в любом IDE.

Angular 2 сломал все в Angular 1. Думаю, Svelte 3 значительно ближе к Svelte 2 в таком сравнении. К тому же, вряд ли дальше что-то будет так координально меняться. Svelte искал свой аутентичный путь и я думаю нашел его.

Если бы многословность была действительно проблемой, люди бы не писали на Angular и на, скажем, Java.

Многие и не пишут) Возможно и по этой причине тоже.

Ну для императивных вызовов в ответ на обновление стейта был lifecycle hook — назывался, если не ошибаюсь, onupdate.

То есть вы не видите различий между computed props и onupdate в Svelte 2? Ни концептуально, ни в удобстве использования?

  export default {
    onupdate({ changed, current }) {
      if (changed.bar && current.bar > 2) {  // отрабатывает на каждый пчих
        alert('Hello world');
      }
      // ... + 100500 условий для других полей
    }
  };


Я не хотел отправлять развёрнутый ответ, потому что мы друг друга не уговорим. Вам всё нравится, а меня не переубедить, пока не будут рабочие плагины для VS Code или WebStorm, умеющие Svelte 3. Вот когда будут, тогда дам второй шанс.

Svelte — это исключительно open-source community-driven проект, за что, кстати, я его очень ценю. За ним не стоят никакие компании, только люди. Если вы заинтересованы в его использовании и видите проблемы с текущими инструментами разработки, то сообщество будет радо вашей помощи! Напишите свой плагин к IDE или помогите улучшить сущесвующий. Нет времени? Тогда подождите, когда это сделают другие. Оценивать что-то по деньгам компаний, это не уважать open-source и труд людей.

Ну вот докатились до передергивания фактов и обвинений в неуважении. Пожалуй, стоит закрыть диалог.

Кстати лайфхак для сторонников DirtyChecking (angular), если в Svelte 3 использовать выделенную переменную как VM (View Model) (let vm = {...};), то эта компонента будет работать как чисто DirtyChecking и все изменения будут отлавливаться и без compound "$:".
Так что DirtyChecking пришел и в Svelte ;)
Это будет работать, да. Единственный момент — это не имеет никакого отношения к dirty checking angular. Более того, это абсолютно ничего не стоит.
Единственный момент — это не имеет никакого отношения к dirty checking
Это как раз и есть dirty-checking, когда проверяются все биндинги на изменения даже если они не менялись (в пределах scope), пример:
<script>
	let scope = {
		name: 'linux',
		foo: () => { console.log('DC'); return 'static';}
	};
</script>
<input type=text bind:value={scope.name} />
{scope.foo()}
scope.foo() будет дергаться каждый раз даже если он не менялся, так же и в ангуляре, только в ангуляре идет несколько проходов — до финального статуса, а тут только один проход, что может дать не консистентное состояние если скомпиллируется.

Более того, это абсолютно ничего не стоит.
Не абсолютно, но не значительно — почти так же как и в ангуляре*.
Слушайте, ну не знаю. В angular помнится был digest cycle и наблюдатели для переменных (всякие там $watch). И там вроде любое изменение значения любой переменной переназначало значения других наблюдаемых переменных.

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

p/s То что фукнция будет деграться можно считать pull-реактивностью)))
Да, в AngularJS есть слушатели на обновление scope($scope.$watch).
$watch помогает прослушивать изменения $scope
function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}


P.s. Я думаю что это не самая большая проблема с производительностью. Самая ужасная проблема сидит перед монитором и пишет в production-коде сортировку пузырьком для того, чтобы найти… максимум в массиве.
В angular помнится был digest cycle и наблюдатели для переменных (всякие там $watch)
Просто другая резализация того же опроса биндингов, а $watch — это фича, в Svelte тоже не плохо бы её заиметь, т.к. computed $: вызывается даже если нет изменений, хотя это легко допилить кому надо.

Тут всего этого нет — просто проверки конкретных условий
В ангуляре эти проверки происходят в цикле, а не линейно развернуто (т.к. добавляются динамический). Да в свелте оно получается легковестней, но разницы вы не заметите т.к. 99%* (большую часть) времени уходит на отрисовку DOM и пр.

p/s То что фукнция будет деграться можно считать pull-реактивностью)))
Хорошо что теперь вы позитивно на это смотрите, а то 2 года назад когда мы спорили про Ractive/angular вы были против подобного ;-)
Просто другая резализация того же опроса биндингов, а $watch — это фича, в Svelte тоже не плохо бы её заиметь

Был в Svelte 1 observe, но не надо его)) Кстати, кроме reactive declarations есть еще хуки beforeUpdate и afterUpdate.

т.к. computed $: вызывается даже если нет изменений, хотя это легко допилить кому надо.

В каком смысле? Он вызывается только если связанная данные изменились.

В ангуляре эти проверки происходят в цикле, а не линейно развернуто (т.к. добавляются динамический). Да в свелте оно получается легковестней, но разницы вы не заметите т.к. 99%* (большую часть) времени уходит на отрисовку DOM и пр.

Если так рассуждать, тогда почти все реализации ± похожи друг на друга. Точнее их можно отнести к очень ограниченному кол-ву способов. Лично мне кажется отличия с ангуляром достаточно существенные. Как нимимум никаких observable нет, просто переменные и topoligical order.

Хорошо что теперь вы позитивно на это смотрите, а то 2 года назад когда мы спорили про Ractive/angular вы были против подобного ;-)

Не, я за push-реактивность как и раньше)) Но если нужна pull, функции в шаблонах примерно это и дают.
В каком смысле? Он вызывается только если связанная данные изменились.
let user = {name: 'a', age: 10};
$: doubleAge = user.age * 2;
Если изменить user.name, то doubleAge вызовется, но он не связан с user.name и изменений в нем нет.

Если так рассуждать, тогда почти все реализации ± похожи друг на друга. Точнее их можно отнести к очень ограниченному кол-ву способов.
Типа того: Observable (frp), DirtyChecking, VDOM, [Property, Zone.js, Proxy] (какие ещё?), и вот уникальная новинка! от Svelte 3 — «статическая» инвалидация (или как назвать?).
Knockout.js: Observable
Vue.js 1: Property + Observable
Recat.js: VDOM ( + всякий flow)
Angular 1: DirtyChecking
Angular 2+: Zone.js + DirtyChecking
Svelte 3: Статическая инвалидация + DirtyChecking ( + фильтрация по корневому объекту, чтобы не все проверять).

Я приписываю DirtyChecking потому что идет не точечный отлов, а проверка списка биндингов (вычисление выражений и вызов ф-ий если они там есть).

Но кроме системы отслеживания у фреймворков ещё разные фичи, так что есть что посравнивать.
Если изменить user.name, то doubleAge вызовется, но он не связан с user.name и изменений в нем нет.

А, это да. Однако в Svelte принято работать с максимально плоскими данными, потому что это не только удобнее, но и работать будет быстрее. Я лично стараюсь выделять объекты в отдельные компоненты и размазывать пропсы по стейту, тем более что с spread-оператором это удобно.

<UserInfo {...user} />

Я приписываю DirtyChecking потому что идет не точечный отлов, а проверка списка биндингов (вычисление выражений и вызов ф-ий если они там есть).

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

<Layout sidebar={<MySidebar/>} />
Так нет, можно так:

<Layout sidebar={MySidebar} />

Жаль, мне в React нравится то, что компоненты и само дерево компонент являются первоклассными гражданами языка. Этот фокус с сайдбаром, к примеру, позволяет избежать излишнего пробрасывания props'ов. https://twitter.com/dan_abramov/status/1021850499618955272?s=20

Мне кажется не стоит выдавать архитектурные просчеты реакта за его достоинства (Дэн вообще мастер в этом). Передавать html-like элемент (реакт компонент) в качестве attribute-like (пропсы) штуки не просто некрасиво, но и совершенно неудобно. Мы же в HTML не далеем штуки вроде:

<main sidebar="<aside></aside>"></main>

Это мерзко. Другое дело Реакт просто неумеет передавать больше одного children.

Однако в стандарте есть вполне адекватное, а главное органичное для HTML средство — слоты. Которые Svelte поддерживает, поэтому ваш пример можно очень просто решить так:

<div class="app">
  <Nav>
    <UserAvatar {user} size="small" />
  </Nav>
  <Body>
    <div slot="sidebar">
      <UserStats {user} />
    </div>
    <div slot="content">
      <Content />
    </div>
  </Body>
</div>
А почему это архитектурный просчёт?

Вообще, если уж лезть в бутылку, то в html атрибутах могут быть ТОЛЬКО строки. Так что тут либо мы чисто в html/web components и у нас в атрибутах только строки, либо мы «крестик снимаем» и передаём в атрибутах что угодно, что возможно в javascript.

Насчёт «неумения передавать несколько children», тут есть момент, «children», это точно такой же атрибут в React как и любой другой. Просто ему присваивается значение body. Для меня было открытием, что можно писать вот так:

const MyButton = props => <button className="myButton" {...props}/>
и кнопке передадутся дети.

А подскажите еще, как в svelte делается что-то типа
<Tabs>
  <Tabs.Tab title="Описание">.....</Tabs.Tab>
  <Tabs.Tab title="Характеристики">.....</Tabs.Tab>
</Tabs>;

чтобы Tabs реализовывал логику переключения табов, показывая N-го ребёнка? Я в примерах не нашёл как можно детей перебирать.
А почему это архитектурный просчёт?

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

<Layout
  navbar={
     <Navbar 
         user={user} 
         cart={cart}
         onLogin={onLogin} 
         onLogout={onLogin} 
      />
  }
  leftbar={
      <Leftbar 
          user={user} 
          settings={settings} 
          <!-- all additional props -->
      />
  }
  content={
       <Content
           filters={filters}
           <!-- all additional props -->
       />
  }
  rightbar={
       <Rightbar 
           filters={filters}
           <!-- all additional props -->
       />
  }
/>

А это вобще перестают быть читаемым. Но на Svelte все еще будет выглядеть нормально:

<Layout>
  <div slot="navbar">
     <Navbar 
         {user} 
         {cart}
         {onLogin} 
         {onLogin} 
      /> 
  </div> 
  <div slot="leftbar">
      <Leftbar 
          {user} 
          {settings} 
          <!-- all additional props -->
      />
  </div> 
  <div slot="content">
       <Content
           {filters}
           <!-- all additional props -->
       />
  </div> 
  <div slot="rightbar">
       <Rightbar 
           {filters}
           <!-- all additional props -->
       />
  </div>
/>


чтобы Tabs реализовывал логику переключения табов, показывая N-го ребёнка? Я в примерах не нашёл как можно детей перебирать.

Через контекст, либо можно через скоуп слотов, но это менее удобно.
Ну не знаю, как по мне, так вариант Svelte одинаков, или даже чуть хуже по читаемости. В React сразу видно, что это у нас props'ы с определённым содержимым. А в Svelte это div с магическим атрибутом. Причём там магия такая замороченная.

<!-- App.svelte -->
<FancyList {items} let:item={item}>
	<div>{item.text}</div>
</FancyList>

<!-- FancyList.svelte -->
<ul>
	{#each items as item}
		<li class="fancy">
			<slot item={item}></slot>
		</li>
	{/each}
</ul>

Вот тут я честно запутался в let'ах. какой item чему соответствует… В React как-то идеологически проще, либо через render-props мы вызываем children как функцию, передавая ей item, либо через Children.map + React.cloneElement передаём в props item, чистый javascript.

<div slot="navbar">
Вот этот div render'ится в итоге? А если мне надо без него?
Ну не знаю, как по мне, так вариант Svelte одинаков, или даже чуть хуже по читаемости. В React сразу видно, что это у нас props'ы с определённым содержимым.

У вас просто уже глаз зареактился видимо. Это называется HTML если что и это язык веба.

А в Svelte это div с магическим атрибутом. Причём там магия такая замороченная.

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

Вот тут я честно запутался в let'ах. какой item чему соответствует…

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

В React как-то идеологически проще, либо через render-props мы вызываем children как функцию, передавая ей item, либо через Children.map + React.cloneElement передаём в props item, чистый javascript.

Я вас умоляю, render-props — это очень спорный костыль, который даже многие реактщики не советуют использовать. Это подтверждает и появления таких штук как react-adopt, например. Придумали его сдуру ребята из React Router и, как мне рассказывали, уже пожалели об этом.

Если вам нравится писать такие портянки, которые и не js и не html, а чудо-юдо-рыба-кит, то я вряд ли вас переубежу. Могу сказать только, что на Svelte можно решить абсолютно все задачи, которые можно решить на React. Однако делается это обычно другим, более нативным к стандарту, способом.
Вообще-то это называется стандарт, почитайте ссылку что я давал выше

это было бы так, если бы Svelte действительно использовал Shadow DOM для его реализации. В данной ситуации – это имитация похожего API средствами Svelte.


Кто-то может сказать, что это плюс – Svelte готовит нас к будущим технологиям, позволяя их использовать уже сейчас. Однако, есть и минус – в реализации Svelte есть отличия от стандарта, <slot item={item}></slot>, что вызовет проблемы при переходе на нативное API, ведь там такого нет.


Поэтому я бы предпочел либо 100% совместимое API, либо что-то совсем отдельное, чтобы не конфликтовать с будущими расширениями ShadowDOM стандарта.


Теперь про React и его пропсы:


Передавать html-like элемент (реакт компонент) в качестве attribute-like (пропсы) штуки не просто некрасиво, но и совершенно неудобно

React не накладывает никаких ограничений на передаваемые значения, и это прекрасно.


<Panel title="My panel">...</Panel>

А теперь нам, допустим, понадобилось добавить иконку в заголовок:


const title = <><Icon />My panel</>;
<Panel title={title}>...</Panel>

Готово! Публичное API компонента Panel осталось обратно совместимым.


В подходе со слотами мы должны заранее решить, что является слотом, а что атрибутом. Если в будущем придет требование, о том что этот компонент должен принимать html-контент, то нам придется переносить значение из атрибута в слот, ломая обратную совместимость или оставить и слот, и атрибут, усложняя API для понимания пользователями.


Я в свое время имел кучу проблем с AngularJS-компонентами при переходе с атрибутов на использование $transclude, так что я точно уверен, что подход React с передачей всего через props (в том числе и children), это и красиво, и удобно для будущих расширений API.

Публичное API компонента Panel осталось обратно совместимым.

function Panel({ title }) {
    return <div>{ title.toUpperCase() }</div>
}

Ну это уже докапывание по мелочам. Подразумевалось, что там будет просто <div>{title}</div>.


А uppercase я бы лучше сделал через CSS.

это было бы так, если бы Svelte действительно использовал Shadow DOM для его реализации. В данной ситуации – это имитация похожего API средствами Svelte.

Svelte действительно будет использовать Shadow DOM для его реализации, если скомпилировать компонента в качестве Custom Element. Вот можете полюбоваться поддержкой стандарта. А код на React явно придется переписывать полностью.

Кто-то может сказать, что это плюс – Svelte готовит нас к будущим технологиям, позволяя их использовать уже сейчас. Однако, есть и минус – в реализации Svelte есть отличия от стандарта, , что вызовет проблемы при переходе на нативное API, ведь там такого нет.

Скоупед слоты появились только в Svelte 3 (Svelte 2 избгал их именно по описанной вами причине) и каюсь, это я завел обсуждение из которого в итоге появились скоупед слоты и контекст апи. Виноват.

Теоретически это может вызвать проблемы при переходе, хотя и не такие серьезные как в React. Однако, так как Svelte это компилятор, то при компиляции в Custom Element он может превращать эту штуки в простые 2way биндинги или еще как-то (пока вопрос до конца не обсужден). Потому что сделано это было лишь для удобства, чтобы не приходилось пробрасовать стейт через верхний компонент, как это приходилось делать в Svelte 2. В любом случае, это гараздо ближе к стандартку и гараздо проще потом привести к нему.

Поэтому я бы предпочел либо 100% совместимое API, либо что-то совсем отдельное, чтобы не конфликтовать с будущими расширениями ShadowDOM стандарта.

В уже прислал вам ссылку выше, так что можете считать, что пока Svelte 100% совместим с тем, что есть. Любые потенциальные проблемы можно решать на уровне компиляции в веб-компоненты.

А теперь нам, допустим, понадобилось добавить иконку в заголовок:

Знаете, для меня это звучит как довольно странный подход. Текст — это текст, а текст с кнопкой уже компонент. Мы не можем точно знать, что внутри Panel изначально делалось с текстом, возможно на него были заасайнены какие-то реактивные переменные или иные манипуляции. Я бы не стал делать как вы делаете в React, это как минимум не безопасно.

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

Был вот такой шаблон Panel:
<div>{title}</div>

Дописывает пару строк:
<slot name="title">
  <div>{title}</div>
</slot>

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

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


Комбинацию атрибут+слот сложнее документировать. Нужно задокументировать, что эти два способа не могут использоваться вместе, слот в этом случае получает приоритет. Как это отразить в тайпингах, я вообще не представляю.
С JSX-пропсами такой проблемы нет, пишем title: string | ReactNode – и готово.

Использование слотов не является самоцелью.

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

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

К сожалению или к счастью (я тоже скептик), но переход на веб-компоненты неизбежен. Другое дело, писать на читых веб-компонентах мы вряд ли захотим в обозримом будущем, поэтому легкие довески типа LitElement или компиляторы в WC типа Svelte/Stencil точно займут хорошие позиции. И опять же, слоты — это удобно, пропсы — это неудобно ;-)

Комбинацию атрибут+слот сложнее документировать. Нужно задокументировать, что эти два способа не могут использоваться вместе, слот в этом случае получает приоритет.

Приоритет всегда определен при таком использовании. Задокументировать не сложно. Мне кажется вы придумываете не реальные проблемы, но при этом упускаете реальные, о которых писал я и даже Дмитрий, когда компонент Panel уже имеет логику завязанную на строковый пропс title. Ваш трюк без проблем сработает лишь для очень dumb-компонентов. Мой сработает для любых кейсов почти без рисков для приложения.

Как это отразить в тайпингах, я вообще не представляю.
С JSX-пропсами такой проблемы нет, пишем title: string | ReactNode – и готово.


Во-первых, мы разделили сущности: title через пропсы, который теперь исключительно string, и кусок произвольного html через слот, который наверное DocumentFragment получается. Изначальные типы параметров (пропсов) сохранены, что есть несомненный плюс. Нет никакой лишней вариативности даже с точки зрения типов.

Во-вторых, нет никаких тайпингов в JS и тем более в HTML. HTML и CSS являются вполне себе состоявшимися языками веба. Поэтому я не понимаю желание многих засунуть все в JS (JSX, Css-in-Js) и работать только с ним. Ну и хватит сувать TS во все дырки, это как минимум не гигиенично.

Особенно это не приятно и не к месту, когда в команде есть настоящие верстальщики, которые знают +100500 способов стилизовать список или чекбокс, но совершенно не интересуются программированием на JS/TS.

Так что тут либо мы чисто в html/web components и у нас в атрибутах только строки, либо мы «крестик снимаем» и передаём в атрибутах что угодно, что возможно в javascript.

<button onclick="alert('Clicked')">Click me</button> — тут только строки, или что угодно?

да, «alert('Clicked')» это таки строка, которую потом браузер интерпретирует как js… Так же как и в web-components, когда я пишу <my-component attr=«myJSVar»/> myJSVar это тоже строка, которую потом интерпретируют как что угодно. Везде свои мелкие DSL'и. stackoverflow.com/a/50416836.
Так и в JSX пропсы это строки. Собственно навскидку и не вспомню, когда были не строки, которые транслятор интерпретирует в соответствии с каким-то синтаксисом.
Нет, в JSX есть два синтаксиса — нормальный <Component prop={someJSExpression}/> и сокращённый для случаев, когда значение это статическая строка или true — <Component prop=«someString» someBooleanProp />. В нормальном варианте значение property — любое валидное javascript expression, которое можно писать в правой части присваивания.

Попробуйте, откройте babeljs.io/repl, выберите preset «react» и введите следующий код
const someVar = 1;
const a = (
  <div
    someProp={someVar}
    someBooleanProp
    someBooleanFalseProp={false}
    someStaticString={"str1"}
    someStaticShortString="str"
  />
);

В случае webcomponents, значение атрибута это именно строка, которую потом надо парсить, вплоть до варианта с JSON.parse

Вы не так поняли. Исходные коды — это сроки в текстовом файле. Одни строки или их части интерпретируются в одном синтакисе, другие — в другом. Хоть в HTML, хоть в JSX.

Нет, я правильно понял. Смысл в том, что для браузера (уже после транспайлера XXX→JS) атрибуты в html/web components это всё еще строки. И их вам самим в компонентах надо парсить. В Svelte атрибуты — это объекты с полями, к которым из шаблона можно получить доступ. В React это еще и куски vdom'а, которые можно вставлять в другие места vdom'а. Лично мне слоты в Svelte или html кажутся костылями из-за того, что нельзя передавать ноды.

Даже браузер интерпретирует значения разных атрибутов по разному. Разными, так сказать, движками. Одни атрибуты парсит движок JS, другие — движок CSS. Добавляя фреймворки мы по сути лишь добавляем дополнительные движки

Хотелось бы посмотреть на средних размеров приложение, с модулями, контролами, роутингом. Хотя бы интернет-магаз. Есть ли где подобные примеры?
Особенно интересует способ хранения и использования разными контролами одних и тех же данных.
Особенно интересует способ хранения и использования разными контролами одних и тех же данных.


Классический пример из React с twitter-подобным интерфейсом, когда в navbar и sidebar используется user пойдет?

~/stores/user.js
import { writable } from 'svelte/store';
import { post } from '~/http.js';

const user = writable(null);

export function login(credentials) {
    post('/login', credentials).then(res => user.set(res));
}

export default user;


~/components/Navbar.svelte
<nav>
{#if $user}
  <a href="/users/{$user.id}">
    <img src={$user.avatar}> {$user.name}
  </a>
{:else}
   <a href="/login">Login</a>
{/if}
</nav>

<script>
  import user from '~/stores/user.js';
</script>


~/components/Sidebar.svelte
<aside>
{#if $user}
  <img src={$user.avatar}> 
  <h1>{$user.name}</h1>
  <p>{$user.bio}</p>
{/if}
</aside>

<script>
  import user from '~/stores/user.js';
</script>


Пример конечно наколеночный, но думаю суть ясна.
Спасибо за пример. А меняются данные так же, импортируем и работаем как с обычной переменной?
Sign up to leave a comment.

Articles

Change theme settings