All streams
Search
Write a publication
Pull to refresh
24
0
Alexey Schebelev @AlexxNB

Fullstack Javascript

Send message

Дело в том, что Svelte — он не React и не Vue. Упаковывает и импортит компилятор. Вам об этом думать не надо. В этом и смысл — чтобы не писать кучу шаблонного кода(т.е. boilerplate), который обязательно должен быть в фреймворке с runtime. В учебнике чуть подробнее про импорты и вложенные компоненты(полистайте вперед немного, там последовательное объяснение и выполнение уроков)

Силами сообщества сейчас пилится Svelte-Native, но пока не для серьёзного применения.

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


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

{obj['field'+n]}

REPL


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

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


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

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

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

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

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

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

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


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

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

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


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


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

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

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


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

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


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


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

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

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

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

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

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


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

    let someButtonClicked = true;

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

{obj.field1}

REPL


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

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


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

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

{obj.field1}

REPL

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

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

Почему не заработает то? Вам почему-то захотелось вставлять именно в `$:`. Может конкретизируете свой пример с реальным кейсом? Попробуем разобраться.

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

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

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


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

{time}

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

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


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

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

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


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

Information

Rating
Does not participate
Location
Петрозаводск, Карелия, Россия
Date of birth
Registered
Activity

Specialization

Fullstack Developer, Embedded Software Engineer
From 80,000 ₽
JavaScript
Svelte.js
Node.js
Docker
Linux