Привет! Меня зовут Кирилл, я уже более 8-и лет занимаюсь веб-разработкой. Несколько месяцев назад мы начали разрабатывать новый проект и у нас возникла задача супер быстрого фронта, при этом сохранив все приколюхи реактивных фреймворков. Встречайте, Sexy framework!
Итак, Sexy framework – это реактивный компилятор и анализатор Javascript кода для создания пользовательских интерфейсов. В отличии от других фреймворков, Sexy спроектирован, чтобы использовать весь нативный потенциал Javascript.
Это означает, что фреймворк не работает в runtime. Всю основную работу он делает в момент компиляции. Фреймворк анализирует ваши компоненты и переводит их в нативный Javascript с очень небольшим оверхедом: максимальный вес бандла всего 3.7kb gzip. (если будут использованы все типы рендеринга, анимации и т.д.)
Вообще, Sexy фреймворк был сделан для работы совместно с серверным рендерингом, где и показывает лучшие цифры по Google PageSpeed. Например, значение FID (Fisrt input delay) при гидратации 500 статичных компонентов занимает всего 50мс, когда у NuxtJs все 180мс, а у Svelte 500мс.
Это было бы не так важно, если бы доля мобильных устройств в интернете не была бы около 68% и дело не только в SEO.
Теперь и для обычных веб-сайтов можно использовать компонентный подход и нормальное тестирование компонентов.
Почему он такой Sexy?
1. Не работает в runtime
Вся основная работа происходит на уровне компиляции, а в итоге вы получаете нативный и супер эффективный Javascript, который позволяет создавать быстрые веб-приложения.
2. Не использует виртуальный DOM
Опять же, в отличии от других фреймворков, вместо виртуального DOM используется нативный DOM.
3. Нет реактивных библиотек
Sexy полностью реактивен, но не использует ни одну реактивную библиотеку и, скорее всего, никогда не будет использовать. Почему? Потому что реактивность делается в момент компиляции. Sexy анализирует весь написанный Javascript код и проставляет реактивные зависимости вручную, за вас и за браузер.
4. Частичная гидратация
Sexy был спроектирован с подходом hydrate first. То есть изначально разрабатывался, чтобы гидратация была максимальна быстрая, дабы повысить FID и TTI. В среднем скорость начала работы на клиенте (при SSR) лучше, чем у других фреймворков минимум в 3 раза.
5. Действительно нативный
Sexy переводит весь код в нативные Javascript инструкции, поэтому sexy-компоненты весят мало.
6. Очень быстрый
Помимо быстрого рендера, Sexy очень сильно улучшает гидратацию, что позволит использовать фреймворк при создании веб-приложений для медленных устройств. Или… Проектировать приложения почти не задумываясь о производительности и всяких костылях в виде ленивой гидратации.
Работа с Sexy framework
Существует два способа начать использовать Sexy фреймворк:
1. Простой – создать веб-приложение и начать писать код
npx create-sexy-app sexy-app
И запустить в режиме разработчика
npm run dev
2. Сложный – Подключить библиотеки вручную и настроить бандлер (webpack)
Подробнее в документации.
Sexy framework
Фреймворк находится в alpha версии.
Синтаксис и однофайловые компоненты похожи на Vue, но есть несколько отличий. Подробнее можете почитать в документации.
Также в проект нуждаются контрибьюторы, core-команда еще не сформирована.
pfffffffffffff
Мм ручное управление реактивностью
kirBurkhanov Автор
Возможно, из статьи не очень понятно. Нет никакой ручной реактивности. Вы работаете с данными также, как и во всех реактивных фреймворках.
Но фреймворк сам проставляет реактивные зависимости вручную в момент компиляции компонента.
Akuma
Ага. Ровно до первого случая когда «щас мы тут изъеб**мся» перестанет работать, потому как кроме разработчика никто не поймет что он там задумал :)
UPD: Прям с ходу:http://sexy-js.ninja/docs/single-file-components/reactivity
> Так делать не стоит, значение изменится, но обновления DOM не запустятся
kirBurkhanov Автор
Так разработчик явно помечает реактивные данные, точно также, как вы это делаете в других фреймворках.
Насчёт того, как делать не стоит, можете уточнить, что имеете в виду?
Пока единственная проблема (но она решаема) это полная реактивность объектов
Akuma
Вы уж определитесь:
> Нет никакой ручной реактивности
> разработчик явно помечает реактивные данные
Это цитата из документации.
MaZaAa
Лол, посмотрите как работает MobX.
kirBurkhanov Автор
Что конкретно вы имеете в виду?
MaZaAa
Вот это настоящая реактивность. Уже дофига лет в JS существуют getters/setters, а с появлением ES6 появилась Proxy, что делает реактивность ещё круче.
codesandbox.io/s/frosty-hawking-jz7gg?file=/src/App.tsx
kirBurkhanov Автор
Во первых, на прямую во фреймворках вы со всем этим все равно не работаете.
Во вторых, вы говорите про то, как реактивность работает под капотом.
Не понимаю вашего «лол» и что я должен понять из приведённого участка кода. А самое главное, что в проекте на текущий момент сделано не так как надо.
MaZaAa
Если взять именно возможности именно JS, то у вас не реактивность, у вас publisher / subscriber причем в явном виде руками, в MobX же это сделано внутри getters / setters в 4ой версии, и в Proxy в 5ой версии.
То есть this.counter++ работает как ожидается, а у вас надо counter(counter() + 1);
Или же this.item.name = 'asd' в MobX работает как ожидается, а у вас надо дополнительно вызывать руками функцию для вызова реакций
kirBurkhanov Автор
Верно. Как указали ниже, обёртку можно и другую использовать, это не проблема.
Проблемы появляются когда используются вычисляемые свойства. Любая реактивная библиотека запускает subscriber, чтобы проставить зависимости. Это добавляет лишнее время на запуск js кода. Когда у вас 10 компонентов — это не критично, но когда у вас много статики, тогда время до интерактивности улетает в небеса.
Riim
Так что на счёт самого алгоритма? Есть ли computed? Есть ли транзакции? Если этого нет, то MaZaAa прав, пока всё плохо.
kirBurkhanov Автор
Computed есть, транзакций пока нет, но тоже добавить реально.
Единственное, что делает Фреймворк, он проставляет зависимости для computed и subscriber без вызова функции на этапе компиляции. Чтобы не вызывать эту функцию в рантайме
Riim
вот с этим как раз больше всего проблем возникнет. Наиболее интересная на эту тему статья на на хабре: Изучаем и реализуем алгоритм работы правильного observer паттерна для react компонентов. Она, конечно, сильно облегчит вам работу, но может не стоит изобретать колесо? Посмотрите на cellx и на mobx. Оба решения отлично подходят для встраивания в фреймворки.
kirBurkhanov Автор
Так Я за, но они все вызывают функции для поиска зависимостей и от этого никуда не деться. Соотвественно просядет производительность, если есть какой то способ, как избежать этого, и вы его вдруг знаете, будет супер.
Riim
Зависимости вычисляются при каждом вызове вычисляемой ячейки. А как вам нужно?
kirBurkhanov Автор
Вычислять зависимости на этапе компиляции, чтобы при запуске на клиенте не выполнилась лишняя работа
Riim
Плохая это идея, посмотрите на следующий код:
При вычислении зависимостей на этапе компиляции они будут прибиты к вычисляемым ячейкам и в последней строке примера вместо 'нет вычисления "c"' будет 'Compute "c"', то есть лишнее вычисление. В конечном счёте на таких лишних вычислениях вы будете терять куда больше производительности. Плюс головная боль при отладке.
nin-jin
В вашем случае лишняя работа будет на рередер при изменении любой реактивной переменной от которой результат может зависеть в принципе, но не зависит в данный конкретный момент времени.
kirBurkhanov Автор
Riim nin-jin
Точно. Что-то я не подумал об этом. Можно попробовать разделить subscriber на 2 этапа:
— «Биндинг» зависимостей
— Работа с DOM
Возможно это решение можно будет прикрутить к Svelte и не нужно будет изобретать велосипед. Попробую, спасиб!
nin-jin
Да дело же не в DOM или биндингах, а в том, что есть статические зависимости, а есть динамические, которые могут быть определены лишь в рантайме. Например, зависимость может быть продиктована вводом пользователя. Я бы предложил реализовать динамический трекинг зависимостей и к нему в дополнение статический анализатор, который выявлял бы статические зависимости и прописывал их явно, исключая из трекинга. Не факт, что это сильно ускорит, конечно, но это будет хотя бы отличительной чертой.
kirBurkhanov Автор
Тестанул. Действительно, я переусердствовал с оптимизацией.
Реактивные либы, если биндить зависимости, но пропускать работу рендера, работают отлично и никакого overhead не добавляют. Походу где-то я накосячил с предыдущим бенчем.
Попробую узнать, как можно внедрить в svelte. Будет круто, если не нужно будет дальше изобретать велик.
Riim
почти совсем не ускорит, в cellx раньше была возможность помечать вычисляемые ячейки как ячейки со статичными зависимостями. То есть зависимости определялись только при первом вычислении и дальше этот механизм полностью отключался. На бенчмарках это вроде и давало какое-то очень скромное ускорение при повторных вычислениях, но там в пределах погрешности было, я пришёл к выводу, что оно просто не стоит каких-то усложнений в коде и плюс нужно такие ячейки ещё самому высматривать. В общем убрал.
Riim
в mobx просто другие обёртки над алгоритмом реактивности. Если сам алгоритм у kirBurkhanov написан хорошо, то не понимаю, что мешает считать такую реактивность настоящей. Дописать удобные обёртки (в тч. Proxy) делов на пару выходных.