Comments 288
В общем, типичная ситуация курицы и яйца: мало пакетов потому что мало пользователей, а мало пользователей потому что мало пакетов.
p/s Под мы я имею ввиду:
Какие подводные камни?
Те же, что и для любых "маленьких фреймворков" — все работает хорошо лишь до тех пор, пока ваши требования к функционалу фреймворка минимальны, и вы идете по оптимальному сценарию. Как только требования вырастают или появляется необходимость в какой-то специфике — начинается беда.
Ну, с-но, для многих проектов это вполне удовлетворительный кейз.
Кроме того, так как 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
Как красиво! Можно даже сказать, философски
count += 1;
Раз к ней было выполнено присвоение, нужно пересчитать DOM и реактивные переменные, связанный с ней. Поэтому дописываем сюда $$invalidate. Более того, внутри как бы еще проверяется, изменилась ли переменная по-настоящему и если нет, то ничего не происходит, поэтому, по идее, сам по себе вызов $$invalidate очень дешевый — мы точно знаем что нужно чекать и в какой момент времени.
А как это вырубать? Например, я тысячу раз меняю count при нажатии на кнопку, но рендер мне нужен только в конце, когда состояние перестало меняться.
А вот обзывать React «костылями» и «уродством» не стоит. Люди используют его в продакшене и довольны, а что вы можете предложить им взамен?
Если интересно, есть и более радикальные идеи как комилировать реакт в императивный код: github.com/sokra/rawact
rawact можно скорее «помянуть» чем упоминать. маловероятно что эту штука когда-либо будет готова взять на себя компиляцию совершенно любого кода на React.
Люди используют его в продакшене и довольны, а что вы можете предложить им взамен?
Очевидно же, Svelte ;-)
Смотря в каком смысле "предложить". Как я уже сказал, настоящая декоративность, если я не ошибаюсь, существовала в Knockout и Angular. Можно было развивать эти идеи. Я думаю, что большинство просто повелось на мнимую простоту Реакта. Для первых шагов было проще понять, как он работает. Но конечная задача все равно гораздо более сложная. И в реальном мире Реакт обрастает кучей всего, что делает этот подход таким же сложным, как Ангуляр, но совершенно тупиковым. Вынос рендера в вебворкер для внесения десятка изменений в DOM этому подтверждение.
А ещё я всем рекомендую посмотреть, как устроен View-слой в уже много лет как полумертвом DerbyJS. Например, там нет костыльного key-property. Когда вы изменяете массив в моделе, на уровне шаблона уже понятно, какие изменения нужно внести в DOM. Зачем ещё что-то сопоставлять по ключу? Хорошая программа (в данном случае фреймворк) строится от интерфейса, а не интерфейс подстраивается под то, как программисту удобнее его реализовать. Имитация декларативности через императивщину — это именно второй случай. Заморачиваться с парсингом декларативного языка долго — проще перезапускать функцию рендера. Но в итоге все равно люди обвешались babel-плагинами.
Большое спасибо за ссылку.
На%$й эту вашу «настоящую декларативность» в первом ангуляре.
Я не очень понял, что именно вам не нравится в 40к вотчерах. Проблема Ангуляра была ровно такая же, как и Реакта, только в Model слое, а не View. Ангуляр диффал данные, Реакт — виртуальный DOM. И то, и другое очевидно будет тормозить. Но дифф данных — это более хорошая архитектура. Потому что было несколько способов решить проблемы, оставив тот же подход в View. А Реакт — это шаг назад, потому что рано или поздно его все равно придётся выкинуть и сделать нормальную декларативность.
Я не очень понял, что именно вам не нравится в 40к вотчерах.
В 40к вотчеров мне не нравится 40 тысяч вотчеров. Это тормозит и это убого.
А Реакт — это шаг назад, потому что рано или поздно его все равно придётся выкинуть и сделать нормальную декларативность.
Какая нахер «нормальная деклративность» в браузере?? CSS? Удачи.
Реактивные подписки, может быть, и есть идеологически более верное решение, но являются ли они решением проблем пользователей? По большому счету, мы пишем на высокоуровневом Javascript в котором происходит очень много всего лишнего, но оправдывает ли это переписывание на ассемблер (WebAssembly в частности)? В большинстве случаев – нет. То же самое и со Svelte.
Но в итоге все равно люди обвешались babel-плагинами.
О чем вы говорите? В стандартом пресете, который используется большинством разработчиков, я вижу только плагины для ES-next синтаксиса и JSX, никакой магии для оптимизации.
Так-то любой декларативный код будет скомпилирован в императивный)
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>
Это не декларативный код. Не обманывайте, ни себя, ни окружающих.
Поясните, пожалуйста, что тут недекларативного, кроме обработчика события?
Привязка декларативна, я имел ввиду сам обработчик. Что за "программная логика"?
<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 или тернарик
Именование хорошо когда оно убирает логическое дублирование кода (когда изменение потребует ручной синхронизации кода в разных местах). В остальных случаях лишнее именование чего либо не было приводит только к усложнению. Например необходимость именовать экшены в редаксе против возможности изменить состояния по месту как mobx-е, или необходимость именовать классы и выносить стили в отдельные местах против возможности указать нужные стили инлайном рядом с тегом или необходимость именовать отдельные роуты в rest-e против возможности вызвать функцию как в rpc. Все это добавляет лишнюю косвенность которая увеличивает количество мест изменения кода в git diff-e а это значит что увеличивается количество неявных связей (программисту нужно держать в голове все места которые требуют изменений при добавлении или изменении/удалении такой-то фичи)
Неистово плюсую. Вдобавок здесь происходит разнесение сильно взаимосвязанных кусков кода по разным местам, что мешает читать код. А ещё с именами нельзя быть сходу уверенным, в скольких местах они могут быть использованы. Т.е. когда просто читаешь код, непонятно, нужно ли дальше держать это имя в голове или можно освободить память.
Если раньше можно было писать весь код в одном файле, то теперь приходится придумывать имена файлам.
Если раньше можно было писать всё в одной процедуре, то теперь приходится придумывать имена методам.
Если раньше можно было писать всё в одну строчку, то теперь приходится придумывать имена переменным.
Я понимаю, очень заманчиво срезать угол и из шаблона лезть напрямую в модель данных. Но для упрощения дальнейшей поддержки лучше это делать всё же через модель отображения, где по имени свойства ясна семантика, и о ней не приходится догадываться анализируя развесистые логические условия.
$my_hello $mol_view
sub /
<= Name $mol_string
hint \Name
value?val <=> name?val \
<= message \
vintage Напишите решение этого примера на treeview для общего развития? Ну и чтобы Balek уже не смог спать спокойно по ночам, зная что такие вещи существуют в нашем мире.
Так тут же дело в абстрактной идеологии. Грубо говоря, сложность программы на Реакте — O(n), хотя проблема решается за O(1). И это заложено в самой архитектуре.
Тупиковая или нет — покажет время. Тупиковость заключается не только в скорости, но и в других аспектах, например React можно использовать совсем без сборки, с помощью вот этой библиотеки получается удобно, регулярно пользуюсь для воспроизведения багов и других простых демок.
С другой стороны, пример rawact показывает, что и API react достаточно декларативное для таких оптимизаций, если будет надо. А будет ли надо? Вот не факт, потому что по моему опыту проблемы обычно скрывались в Redux и аналогичных стейт-контейнерах, а не в VDOM. Чинить надо там где сломалось, а не там где это кажется легко
Видимо у нас очень разные определения тупиковости. Потому что мне время (первые минут 30) уже все показало. Например, когда для оптимизации, нужно в shouldComponentUpdate внести то, что и так очевидно из render-функции. Или необходимость указывать key-property при рендере массива. Или перечислять значения вторым аргументом в useCallback. Эти проблемы нужно решать в фундаменте, а не плагинами к бабелю. Тогда будет архитектура, а не набор костылей.
Потому что мне время (первые минут 30) уже все показало
Я говорю про долгосрочную перспективу, с несколькими годами разработки, миграциями между мажорными версиями, рефакторингом и поиском новых кадров. Это и за час в докладе не покажешь.
В докладе Rich Harris показал один утрированный пример. Много ли пользователей с этим столкнутся? Не факт, я написал довольно много развесистых реакт-компонентов и ни одного shouldComponentUpdate. И без этих приёмников все работает нормально и поэтому костылями мне не кажется
Если бы последние пять лет сообщество вкладывалось в развитие настоящей реактивности, то были бы защиты от дурака.
Сообщество вкладывалось в развитие настоящей реактивности с 2010 года, когда вышел AngularJS, Knockout и Ember. Мне, например, трех лет эквилибристики с scope.$apply хватило, чтобы понять, что «настоящая реактивность» – это как минимум не тривиальная задача.
Не подскажете, в чем там проблема? Приходится явно указывать ангуляру, что данные обновились?
Ну и наличие оберток на половину стандартных функций вроде fetch/setTimeout/setInterval, чтобы этого не делать в ответ на AJAX-запрос/таймаут.
В реальности обязательно встретятся разработчики которые намудрят с $: синтаксисом (например) и убьют всю производительность.
В теории, возможно. На практике, пока не удавалось, хотя мы активно используем вычисляемые свойства и даже функции. Думаю, что возможно дело в этом:
В реакте хватает систем «защиты от дурака» и предупреждений, а как с этим дела обстоят в Svelte?
Никак, Svelte просто хорошо работает:
Надеемся на лучшее, но на случай проблем должны быть предусмотрены какие-то действия, особенно если в проекте работает несколько разработчиков, с разным уровнем квалификации
Не убедили. «Svelte просто хорошо работает» звучит примерно как «у нас никогда не бывает багов».
Согласен, именно так и звучит.))) Но не знаю как еще кратко высказать ту мысль, которую автор Svelte высказывал почти 40 минут. Можно еще наверное так, единственный способ сделать что-то быстрым и надежным — избавиться от всего лишнего. В этом смысле сравнение моторов Теслы и ДВС, имхо, очень показательно.
Надеемся на лучшее, но на случай проблем должны быть предусмотрены какие-то действия, особенно если в проекте работает несколько разработчиков, с разным уровнем квалификации
Тут все просто, что-то работает не так как надо, смотрим итоговый код, находим узкое место, пишем issue или PR. Мне кажется это одна из ключевых фишек Svelte. Так как рантайма практически нет (да вот он весь в приципе), то остальное это кодогенерация для каждого конкретного случая и ее всегда можно легко прочитать, понять как работает и придумать как сделать, чтобы работало лучше. В отличии от черных ящиков в рантайме, куда ты передаешь на вход что-то и адская машина VDOM пыхтит и пытается делать свое дело для каждого кейса на свете.
P.S. Если задача — что-то донести до оппонента, то голословные выпады точно не достигнут цели….
показывает отличные результаты.
Real world example please.
Типа, вот тут реакт тормозит, а свилт решает.
Посмотрите видео.
Посмотрел, ниче принципиально нового в сравнении с реакт не появилось — реакт все также тормозит, ужасает по размеру и кол-ву кода.
Вижу Vue и Angular обновились и немного подтянулись, но там просто какая-то старющая версия Svelte и уже есть планы обновить прилагу до Svelte 3. Тогда и будем смотреть.
Про размер кода — согласен, разница существенная.
Вообще, первый пункт всегда был немного 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 либы довольно быстро посаживают память на дешевых моделях (коих абсолютное большинство рынка).
Другое дело, если начинать новый проект, то я считаю, что как минимум стоит задуматься над теми вещами, о которых говориться на видео и возможно попробовать Svelte. Пока от тех, кто его попробовал на практике, я не слышал ничего отрицательного.
Кстати, по поводу легаси проектов, есть еще интересный хак со Svelte. Так как он не монолитный и фактически не имеет специфического рантайма, его компоненты довольно просто можно интегрировать в проект на любом другом фреймворке (фактически это как внедрение любой ванильной либы). Так вот, в нескольких проектах на Ractive, нам уже пришлось переписать несколько Ractive компонентов на Svelte, потому что они притормаживали.
Могу сказать, что это сделать довольно просто. Я даже статью на эту тему писал.
Ну, пусть так. Тогда задача сводится к вопросу: «как, имея функцию 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, начинают трахаться с этой автомагией, раскуривают все внутренности и хитрости фреймворка, героически проблемы решают. И только пройдя этот диалектический цикл, они понимают где и как их кинули.
Никто и не предлагает решать эту задачу в общем виде. Это нужно было бы только разработчикам реакта, чтобы не перевычислять рендер-функцию полностью. Нормальные люди используют декларативные шаблоны, имеющие разумные ограничения, потому с лёгкостью обновляемые инкрементально.
потому с лёгкостью обновляемые инкрементально.
Никто еще не придумал никакого хорошего способа "с легкостью инкрементально" обновлять шаблоны. Все время всплывают какие-то недостатки.
<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
при его изменении
Так не работает:
$: now = Date.now();
$:
отслеживает лишь изменение переменных из состояния компонента, входящих в соответствующие выражения. Очевидно в Date.now() нет таких переменных. Её вызывать надо, чтобы получить от неё что-то.
Т.е. ваш пример выглядит примерно так:
<script>
let time = 0;
setInterval(()=>time=Date.now(),1000);
</script>
{time}
Про работу со стором и функциями в нём лучше расскажет документация
const getData = () => 'x';
$: double = () => getData() + getData();
Так тоже не работет, видимо в голове нужно будет держать где «computed», а где обычные ф-ии.
Тут тоже ничего не изменяется, почему double должен как-то изменяться?Оно как минимум должно запускаться, но нет.
Мы можем просто использовать языкПросто взять и вставить «кусок js» не получится, если у меня вызывается какая-то библиотека в коде, то нужна адаптация.
Не должно там ничего запускаться. В $:
должна быть хотя бы одна из переменных стейта. По определению. Тут же умный компилятор не увидел ни одной переменной в выражении и просто не стал включать в бандл ненужный код. Если бы вставили в разметку {double}
то компилятор бы ещё и предупредил, что такой переменной нет. $:
— это не переменная, чтобы присваивать ей значения функций, это конструкция для перезапуска выражения при изменении входящих в него переменных стейта.
какой тип API был бы лучшим для нас… и поняли, что лучший API — это отсутствие API. Мы можем просто использовать языкПоэтому «просто использовать язык» — нужно с оговоркой, т.к. это все же «апи».
Но в целом идеи фреймворка очень интересные, посмотрим насколько он станет популярным, думаю первый его конкурент — vue (по типу отслеживания).
<script>
import { simple as format } from 'timeago-simple';
export let date = new Date();
</script>
<h1>Updated {format(date)}</h1>
Рабочий пример
Это похоже на «взять и вставить рабочий кусок 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.
«Красота требует жертв», хотя там и жертв то нет при правильном подходе, вообщем минусы есть в каждом фреймворке — вы просто выбираете те минусы которые для вас менее важны.
А что за такой «правильный подход»? В реакт? Не думаю. Пока не вижу чем я жертвую, когда пишу $: — очень удобно и декларативно.
С некоторого времени с интересом наблюдаю.
без лишних затрат и сложности использования прокси или аксессоров. Это просто переменная.Зато добавился закулисный вызов функции и компилятор. К инкременту. Если он внутри цикла, лишний код будет в каждой итерации.
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 штука на Хабре и та «без комментариев». На Хабре с этого года, а где раньше были? Вы может школьник-троль какой-то? Тогда все в целом становится на свои места.
Точно хотите гнуть такую линию дальше?
О да, могу гнуть вас дни на пролет.
Если что-то является решетом, от архитектуры до реализации, зачем такое использовать.
Ну так, свою реализацию в студию. Ну или хотя бы что-то чужое, что вы считаете рабочим. Посмотрим, разберем по полочкам. К сожалению, но умничать со стороны всегда проще.
Тут ведь как получается, одни выдвигают гипотезы и пытаются стоить производительные решения на Virtual DOM, другие выдвигают другие гипотезы и делают штуки вроде Incremental DOM. В этом же время, у Svelte другая гипотеза, а что если просто убрать все лишние абстракции и тупо использовать DOM?
Ведь на самом деле у нас никогда не получалось писать на читом DOM сложные приложения, потому что в итоге это настолько неудобно, что мы неминуемо начинаем оборачиваться абстракциями, чтобы просто не писать настолько императивный, прямолинейный и узко-заточенный код, который генерирует Svelte. Для человека это не приятно, для комплятора разницы нет.
Я это называю «augmented intelligence for web apps» — когда человек делает то, что у него хорошо получается на высоком уровне абстракции, а машина делает то, что хорошо получается у нее.
По поводу, requestAnimationFrame, так и есть. Микротаски конечно же запускаются асинхронно, а не сразу как произошли изменения в стейт.
Помню когда у React появились Fibers, они как бы решали как раз такие проблемы и была еще демка с треугольниками Серпинского, типа вах у нас теперь такое работает. Но на Svelte это просто работает и все. И не надо туда никаких Fibers и shouldComponentUpdate.
Вот фигня какая.
По поводу, requestAnimationFrame, так и есть. Микротаски конечно же запускаются асинхронно, а не сразу как произошли изменения в стейт.
То есть следующий обработчик события будет оперировать не консистентным состоянием, если он успел вызваться раньше следующего фрейма. Это как раз то, что что все так "любят" первый Ангуляр.
Ну а остальные вычисления (все эти "автораны" в ${}
) получается будут зазря отрабатывать синхронно. Модель реактивности с "ячейками" позволяет откладывать вычисления на неопределённый срок, пока кому-то не потребуется результат.
Модель реактивности с «ячейками» позволяет откладывать вычисления на неопределённый срок, пока кому-то не потребуется результат.
Не спорю. Уверен внутри $mol все сделано очень хорошо. Несмотря на то, что мы обычно друг другу апеллируем, а я иногда слегка тролю $mol, но я уверен, что вы профессионал и вижу что часто говорите толковые вещи. Доклады ваши понравились.
Не исключаю, что в $mol какие-то вещи внутри реализованы лучше чем в Svelte, хотя и концепт разный совсем. Лично для меня основная проблема $mol — это присловутый DX. В этом смысле Svelte для меня выглядит значительно проще и понятнее, но это, сами понимаете, на любителя.
svelte.dev/repl?version=3.1.0&gist=38667c3b7f224c1187e0dbb6238bee52
PaulMaly почини мой gist, если я еще не понял как объяснить мысль svelte.
Предположение было такое, что sheep всегда держит актуальное состояние i.
По-моему это поведение никак не связано со 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-а.
Абсолютно нормальная проверка или вы предлагаете бегать вглубь объекта и проверять все его свойства? Если вы не в курсе в JS нет способа понять, что какое-то свойство внутри объекта, изменилось.
А можете пояснить как cd для структур работает? Вот я передал объект в пропсы, объект поменялся, запустилась ваша invalidate, но ее же теперь надо запустить для всех полей во вложенных компонентах? Это как все осуществляется?
Немного абстрактно напишу, как я себе это представляю, так как я предпочитаю под капот не лазить. Работает да и ладно… (С настоящей машиной также поступаю =))
invalidate
помечает, что изменился объект, не нужно метить все поля по отдельности.
Например, при сборке компонента компилятор нашёл, что в разметке и в коде внутри <script>
из всего объекта используется только поле obj.name
. Так при инвалидации obj
Svelte и проверяет только изменилось ли значение obj.name
, весь объект целиком его не интересует.
Ну так, получается, если объект изменился, то тогда все дерево компонент, которое от него зависит будет проверяться обычным цд как в ангуляре?
Ну это как-то в данном случае не особо согласуется с "точечными изменениями где надо" и все такое.
Отнюдь, 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}
что тут сложного при компиляции найти эти моменты
При компиляции этих моментов еще нет. Искать просто нечего.
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}
Компилятор знает, что 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 работает. Или хотя бы спросить их на эту тему. Я уверен у них есть железные аргументы.
не понял, почему true?
Ну объект поменялся.
При каждом тике просто запускается проверка всех "суперпупердешёвых" условий
Но вы не можете никак узнать, какие условия проверят надо, а какие — нет, не пробежавшись по дому. Так как вы не знаете, какие ноды присутствуют, а, с-но, не знаете — какие переменные биндятся в темплейтах.
Вы видимо не понимаете о чем речь. Вот есть у вас компонент foo в котором есть объект obj который прокидывается пропсом в темплейте:
<bar yoba={obj}></bar>
и вы меняете поле в этом obj: obj.field1 = 'yoba'.
У компонента bar тоже есть темплейт и там что-то биндится. В этм теплейте — тоже вызываются какие-то компонеты (в них другие и т.д.) и в них тоже что-то биндится.
Так вот — как только вы изменили поле внутри obj (или сам obj), то любая забинженная внутри темплейта bar или внутри какого-либо другого темплейта (рекурсивно и до самого конца) переменная могла измениться. Вы никак не можете заранее скзаать какая — могла любая.
И у вас два варианта:
- вы просто перерендериваете весь дом для bar целиком (на самом деле в итоге вы перерендериваете практически весь сайт каждый разпри таком подходе) — подход ангуляра
- вы перебираете поочередно все биндинги и смотрите, какие из них изменились, делая точечные апдейты (при этом вы не можете заранее написать сгенерировать портягу ифов, т.к. не знаете какие у вас там в рантайме будут ноды, сколько их и т.д.) — подход реакта
ну и еще вы можете просто проигнорировать чендж, не обновлять и ваше приложение не работает
так какой из этих вариантов в svelte реализован?
Ох, я не могу знать луше тех, кто хотя бы лазил под капотом. Могу немного потеоретизировать на основании того, что я выше писал.
- Вариант точно нет, Svelte не бегает по DOM в рантайме.
- Похоже на него, за исключением того что именно портяга 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]}
Я уверен, вы больше меня разбираетесь в Javascript, не поленитесь посмотрите те несчастные 60 строк скомпилированного кода приложения, и может быть вы лучше расскажете как это всё работает.
Ну и вот я еще один пример набросал — тут ну вообще во время компиляции про структуру объекта ничего не известно, неправда ли?Тут весь «obj» будет инвалидирован, не важно что вы там поменяли.
Т.е. берется корневое имя переменной, например если вы меняете только «obj.user.status.name», то инвалидируется весь «obj», поэтому ваш пример будет нормально работать.
А вот что может в принципе измениться в объекте и как принять решение о том, что это действительно нужное для обновление 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 не было и не может быть в любом другом, не компилируемом, фреймворкеАнгляры делают компилляцию на лету, поэтому там тоже самое но в другом виде.
В этом плане не важно в какой момент откомпилировалось, главное что отрабатывает именно скомпилленый кусок.
```
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
что 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 других полей.
Анализ шаблонов позволяет заранее вычислять какие ноды нужно обновить для какого изменения.
Не позволяет, это невозможно. На этапе компиляции такой информации просто не существует. Зачем вы пишите вещи, которые заведомо ложны? Не понятно.
На этапе компиляции у вас даже нет информации о том, какие вообще ноды будут в вашем приложении. Как вы собрались анализировать изменения того, о существовании чего не знаете?
В рантайме 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 и перерисовка). В иви по крайней мере точно будет так, т.к. там генерируется цельная ф-я апдейта.
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 почти ничего не стоит.
Из плюсов:
- Низкий порог вхождения, коллеги сказали что можно сразу понять как это всё работает, и новичок в отрасли может за 1 день вкурить учебник (я сам не из фронтенда). Учебник кстати очень классный, и на русском языке ru.svelte.dev/tutorial/basics
- Модульность и CSS инкапсуляция по-моему одна из лучших
- Быстрый и ванильный
- Легкий и независимый
- Меньше кода в сравнении с аналогами
- Анимации
Считаю кандидатом #1 в качестве киллера React и VueJS в будущем.
Одним из критериев при выборе чего-либо служит наличие дружественного комьюнити. Вы тут занимаетесь популяризаторством фремворка или антирекламой?
Мне кажется дружественно будет, прежде чем размещать вопросы к публикации, сперва ознакомиться с материалами публикации, включая видео-материалы, если они являются необъямлемой частью публикации. Тем более повторение одних и тех же вопросов, на которые люди, посмотревшие видео, уже получили ответы.
Тем более если есть возможность получить ответы и разъяснения непосредственно от автора. Потому что ни я, ни кто другой не расскажут лучше, имхо.
Когда данные обновляются слишком часто, лишние перерисовки могут приводить к проблемам с перфомансом на сложных страницах (как правило из-за нешустрого обмена данными DOM <-> JS или layout trashing). Универсального решения для общего случая я пока не встречал. Есть различные компромиссы. Многие фреймворки предоставляют интерфейсы для решения проблемы вручную. Еще одним вариантом служит VDOM с реиспользованием существующей структуры DOM при обновлениях.
Из презентации следует, что автор считает эпоху сложных десктопных страниц пройденной, а будущее за эмбедед с примитивными экранами. Если svelte ориентируется исключительно в эту нишу, то отказ от сложных механизмов обновлений DOM выглядит разумным компромиссом. Если нет было бы интересно услышать про опыт использования на десктопных сайтах.
По поводу shouldComponentUpdate
Общая мысль прекрасно освещена вот тут и на этом слайде:
Дружественно будет подавать материалы в разных видах :) Я сейчас даже не про людей с особыми потребностями.
Многие люди плохо воспринимают видео-"документацию" в принципе (я проводил эксперименты над собой: больше трёх раз разница для родного языка в знакомом контексте), а многие часто не могут найти комфортных условий, чтоб видео посмотреть.
Кроме того, российское комьюнити и конкретно AlexxNB приложили очень много усилий для того, чтобы перевод документации и туториалов на русский язык был первым среди локализаций (в данный момент существует только английская и русская версии). Надеюсь для вас это окажется максимально комфортным!
Хотел написать статью о своем опыте со Svelte и о разнице с Vue, но новая версия отличается настолько, что уже смысла нет. Вручную обновлять не хочется, а утилита для апгрейда еще не вышла.
Больше всего понравилась инкапсуляция 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 без сильных изменений бэка не сделать.
Может я неправильно понял, но сложилось впечатление, что имеющийся бэкенд с API так не прикрутить, использование сервера из фреймворка обязательно.
Возможно вы не очень знакомы с изоморфными веб-приложениями, но там обычно используется 3-х звенная архитектура с frontend-севрером на NodeJS и backend-сервером на чем угодно. То есть ваш существующий бэк на PHP вообще можно не трогать, если там уже есть REST API. Немного не скромно, но могу посоветовать свой доклад по этому поводу с FrontendConf, либо вот выпуск RadioJS#55. Там тоже эта тема затрагивалась.
Ну я делал приложения с фронтом на реакте с бэкендом на PHP, который обращался к node.js рендер-серверу, когда это было нужно (при первом заходе на страницу), подготавливая все данные для рендеринга заранее. Это показывало лучшее результаты чем направление запрсов на nodejs с дерганием с него методов API.
За ссылки спасибо, посмотрю.
Ну я делал приложения с фронтом на реакте с бэкендом на PHP, который обращался к node.js рендер-серверу,
Да, это один из способов, который я также упомянал в докладе для легаси проектов.
В целом, Sapper — это конечно когда нода стоит фронтом, но и схему, когда нода сбоку, можно легко реализовать на Svelte.
Там не tsx в шаблонах.
Автор фреймворка сам любит TS, но поддержки пока нет.
Поддержки в шаблонах нет, прежде всего потому что сам TS не поддерживает подобную кастомизацию. Даже Angular, который ставит на TS все, имеет проблемы с шаблонами, насколко мне известно.
В скрипте можно уже сейчас использовать TS с помощью препроцессора: github.com/PaulMaly/svelte-ts-preprocess
Svelte 3: Переосмысление реактивности