Как стать автором
Обновить

Комментарии 61

Мм ручное управление реактивностью

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

Но фреймворк сам проставляет реактивные зависимости вручную в момент компиляции компонента.
Ага. Ровно до первого случая когда «щас мы тут изъеб**мся» перестанет работать, потому как кроме разработчика никто не поймет что он там задумал :)

UPD: Прям с ходу:http://sexy-js.ninja/docs/single-file-components/reactivity
> Так делать не стоит, значение изменится, но обновления DOM не запустятся

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


Насчёт того, как делать не стоит, можете уточнить, что имеете в виду?


Пока единственная проблема (но она решаема) это полная реактивность объектов

Вы уж определитесь:
> Нет никакой ручной реактивности
> разработчик явно помечает реактивные данные

Насчёт того, как делать не стоит, можете уточнить, что имеете в виду?

Это цитата из документации.
Лол, посмотрите как работает MobX.

Что конкретно вы имеете в виду?

class State {
    @observable counter = 1;

    increment = () => {
        this.counter++;
    };
}
const state = new State();

export const App = observer(() => {
    return (
        <div>
            <h1>Counter: {state.counter}</h1>
            <button onClick={state.increment}>incr</button>
        </div>
    );
});


Вот это настоящая реактивность. Уже дофига лет в JS существуют getters/setters, а с появлением ES6 появилась Proxy, что делает реактивность ещё круче.

codesandbox.io/s/frosty-hawking-jz7gg?file=/src/App.tsx

Во первых, на прямую во фреймворках вы со всем этим все равно не работаете.


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


Не понимаю вашего «лол» и что я должен понять из приведённого участка кода. А самое главное, что в проекте на текущий момент сделано не так как надо.

Если взять именно возможности именно JS, то у вас не реактивность, у вас publisher / subscriber причем в явном виде руками, в MobX же это сделано внутри getters / setters в 4ой версии, и в Proxy в 5ой версии.

То есть this.counter++ работает как ожидается, а у вас надо counter(counter() + 1);
Или же this.item.name = 'asd' в MobX работает как ожидается, а у вас надо дополнительно вызывать руками функцию для вызова реакций

Верно. Как указали ниже, обёртку можно и другую использовать, это не проблема.


Проблемы появляются когда используются вычисляемые свойства. Любая реактивная библиотека запускает subscriber, чтобы проставить зависимости. Это добавляет лишнее время на запуск js кода. Когда у вас 10 компонентов — это не критично, но когда у вас много статики, тогда время до интерактивности улетает в небеса.

Так что на счёт самого алгоритма? Есть ли computed? Есть ли транзакции? Если этого нет, то MaZaAa прав, пока всё плохо.

Computed есть, транзакций пока нет, но тоже добавить реально.


Единственное, что делает Фреймворк, он проставляет зависимости для computed и subscriber без вызова функции на этапе компиляции. Чтобы не вызывать эту функцию в рантайме

транзакций пока нет, но тоже добавить реально

вот с этим как раз больше всего проблем возникнет. Наиболее интересная на эту тему статья на на хабре: Изучаем и реализуем алгоритм работы правильного observer паттерна для react компонентов. Она, конечно, сильно облегчит вам работу, но может не стоит изобретать колесо? Посмотрите на cellx и на mobx. Оба решения отлично подходят для встраивания в фреймворки.

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

Зависимости вычисляются при каждом вызове вычисляемой ячейки. А как вам нужно?

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

Плохая это идея, посмотрите на следующий код:


let a = cellx(true);
let b = cellx(1);
let c = cellx(() => {
    console.log('Compute "c"');
    return a() || b();
});

c.subscribe(() => {});

b(2);
// => Compute "c"

a(false);
// => Compute "c"

b(3);
// нет вычисления "c"

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

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

Riim nin-jin

Точно. Что-то я не подумал об этом. Можно попробовать разделить subscriber на 2 этапа:
— «Биндинг» зависимостей
— Работа с DOM

Возможно это решение можно будет прикрутить к Svelte и не нужно будет изобретать велосипед. Попробую, спасиб!

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

Тестанул. Действительно, я переусердствовал с оптимизацией.

Реактивные либы, если биндить зависимости, но пропускать работу рендера, работают отлично и никакого overhead не добавляют. Походу где-то я накосячил с предыдущим бенчем.

Попробую узнать, как можно внедрить в svelte. Будет круто, если не нужно будет дальше изобретать велик.
Не факт, что это сильно ускорит

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

Вот это настоящая реактивность.

в mobx просто другие обёртки над алгоритмом реактивности. Если сам алгоритм у kirBurkhanov написан хорошо, то не понимаю, что мешает считать такую реактивность настоящей. Дописать удобные обёртки (в тч. Proxy) делов на пару выходных.

Код на гитхаб закрыт. :(
Проект публичный, странно. Через инкогнито код доступен.

github.com/sexy-framework/sexy

Звучит очень сексуально. Но контрибьютить без документации, в особенности без знания дизайна и "философии" фрейма достаточно сложно. Пример аля hello world ничего толком не показывает.

Конечно, проблема в том, что я один, не успеваю все сделать. Для начала хотелось бы понять, нужно ли данное решение кому нибудь кроме меня, прежде чем делать production-ready решение.


Данная статья, alpha версия и дока как раз, чтобы проверить, нужно ли «ещё одно» решение для рынка.

Совершаете ту же ошибку, что и Svelte на старте, без поддержки TypeScript — все это даже смотреть бессмысленно.

Синтаксис добавить не проблема. Главное Это понять нужно ли новое решение, утвердить весь синтаксис и довести Фреймворк до production

Синтаксис не нужно добавлять. Нужно сразу писать на TS.
«Потом» поддерживать @types — так себе занятие будет.
Добавьте, пожалуйста, TypeScript, раз уж это так быстро, а то вот видите ли у некоторых проблема. Angular вот до сих пор не справился, хотя заявили уже много лет назад.
А что именно в Ангуляре не так с поддержкой ТС?
Ну я так понимаю проблема в шаблонах, там и с типизацией беда, а поддержки синтаксиса вообще нет.
Видимо не совсем ясно выразился.

Я имел ввиду, что в шаблонах нет поддержки синтаксиса TS, есть элвис-оператор и все, нельзя использовать привидение типов и т.п.:

(item as SomeClass).prop1


И в input-binding можно подсунуть совсем не тот тип, что описан в компоненте
Согласен. Тут пока ещё всё не очень. :(
Иногда этого не хватает.

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


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

Да это ж Svelte какой-то! На минималках…

Почти так оно и есть, только Гидратация работает быстрее раз в 10.

Svelte в своё время тоже работала «быстрее». А потом её взялись доводить до ума и до удобства использования, и вот уже оба-на, «в десять раз быстрее» кто-то другой. Правда, если этого другого довести до ума и удобства использования — фиг знает, что там получится.

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

Удобство разработки важнее скорости, тем более проблемы со скоростью могут проявляться только в очень очень специфичных проектах и на очень очень слабых устройствах. Поэтому ставить скорость на самое верхнее место, не совсем корректно, т.к. жертвуя удобством разработки эта скорость нафиг не сдалась. Везде нужен баланс, например React + Mobx меня полностью устраивает в удобстве и в производительности как на десктопах, так и на мобилках.

А можете показать пример проекта, где производительность реакта Вас устраивает?

Любой проект написанной мной/командой возглавленной мной работает замечательно и быстро, заметте именно в связке с MobX, даже вместо локального состояния компонентов MobX. Голый реакт или реакт + redux(и ему подобное) это дно, я знаю.

Верю, но хочется увидеть живой пример, цифры pagespeed

Подскажите, есть ли роутинг? Я не фронтендер, но вроде для svelte он делается на стороне сервера (sapper), а в модных фремворках-клиента.

Есть, роутинг пока очень простой. Но он везде делается и там и там, где есть ssr

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


  • разный синтаксис обращения к переменным в яваскрипте и шаблонах, даже в разных местах шаблона он разный.
  • двусторонний биндинг только для свойства value. Для html это может и сойдёт, но не для пользовательских компонент.
  • перенос компонента между родителями приводит к его полному ререндеру.
  • судя по всему любое состояние, что находится вне скоупа компонента, не отслеживается — это источник множества багов.
1. Согласен
2. Двусторонний биндинг можно кастомизировать с помощью своей директивы
3. Так и есть, как часто вообще используется «ручной» перенос?
4. На данный момент так и есть, нужен свой MobX, Vuex и т.д.

Проблем много, но они решаемы. Вопрос только нужно ли новое решение… И насколько остро стоит вопрос производительности при запуске фреймворка. А-ля замена «jQuery» для сайтов с изолированными компонентами и нормальным тестированием.

2) Писать по директиве для каждого параметра каждого компонента, где нужна двусторонняя связь — довольно утомительно.
3) В рамках списка переносы тоже ведь не в ручную делаются. Достаточно уметь идентифицировать компонент не в рамках одного списка, а глобально.


Вообще, если вы вообще задаётесь вопросом "а нужно ли?", то не нужно. Так вы слона не продадите. Сообщество не поддержит ещё один велосипед, даже если он будет лучше по всем показателям. Конкретно по производительности, пользователи уже привыкли, что сайты у них открываются по 10 секунд. Поэтому и разработчики не особо парятся на эту тему.

2. Согласен
3. У меня в списке элементы заново не рендерятся. Так-то можно через привязку к ключу реализовать.

Конкретно по производительности, пользователи уже привыкли, что сайты у них открываются по 10 секунд.


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

Потому что какой сайт ни откроешь — ждёшь по 10 секунд. Типичная картина выглядит как-то так:



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

О! Я тоже на днях новый фрейморк запилил, (уже ни дня без новых фреймворков :)
Сделал аналогичную кнопку с главной страницы, получилось 1кб виджет против 5кб на sexy-js

У вас есть пример TodoMVC для сравнения?, у меня оно весит всего 2.7kb

5кб это из за роутинга и глобальных компонентов, которые добавлены в SSR

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


Подход с innerHTML и template тоже использую.

Название фреймворк затруднит, по-моему, внедрение в серьёзных компаниях.

А чем вас не устроил Sinuous? Такой же маленький и быстрый. Есть транзакции, гидратация и вес меньше, чем у вашей библиотеки.

По коду на гитхабе видно, что вы много оттуда позаимствовали. Вот мне и любопытно, какие были фундаментальные причины кроме NIH.
Оттуда было позаимствована их вариация map + идея с пустыми textNode.

Гидратация в sinuous очень медленная, хуже чем во Vue.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации