🔥

Тред (Илья Лесик)


Попробую раскрыть тему, в которой довольно немало покопался, пока разрабатывал github.com/react-figma/re… - Реакт рендереры и реконсиляция. Тред.

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

Самый известный пример это React Native reactnative.dev - на котором можно разрабатывать приложения под iOS и Android, используя нативные виджеты операционных систем.

React Native это реакт-рендерер, который разрабатывается фейсбуком, но есть множество сторонних

Например, Ink github.com/vadimdemedes/i… - позволяет писать консольные утилиты на React

React Hardware github.com/iamdustan/reac… позволяет управлять железками (например, Arduino) с помощью React

github.com/pmndrs/react-t… - реакт рендерер для Three.js, позволяет крутить 3D-модельки

github.com/diegomura/reac… - рендерер реакта в PDF

Есть целое семейство рендереров в редакторы для дизайнеров, первым из которых был github.com/airbnb/react-s…

Более подробно о разных реакт-рендерарах можно почитать в моей статье dev.to/lessmess/react…

А наиболее полный их список есть тут github.com/chentsulin/awe…

Столь большую гибкость обеспечивает библиотека npmjs.com/package/react-…

Это пакет, который позволяет писать свои собственные рендереры. В нем хост средой называется среда, в которой должен работать этот реакт-рендерер

Для того, чтобы описать, как работать с конкретной хост средой, требуется описать хост-конфиг
notion image

Библиотека react-reconciler использует паттерн Visitor, то есть при обходе дерева реакт-элементов будут вызываться методы хост-кофига, на вход которым будем подаваться элементы по одному

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

Например, createInstance будет вызван, когда требуется создать новый элемент. На вход придет тип элемента (например, div в вебе) и его пропсы
notion image

Можно ли написать реакт рендерер без библиотеки react-reconciler?

В теории да, в mvp библиотеки react-figma и в старых рендерарах вроде react-sketchapp (на момент ее создания еще даже не существовало react-reconciler), так и было сделано

Когда мы только, начинали разрабатывать свой рендерер, первая наивная реализация выглядела примерно так: gist.github.com/ilyalesik/0a58…
notion image

После того, как babel (или ts-компилятор) трансиплирует jsx, получится множество вложенных вызовов функций React.createElement

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

Это будет некое дерево, которое можно попытаться рекурсивно обойти. В поле type элементов этого дерева будет приходить тип реакт-элемента.

Это может быть строка для нативных элементов, или функция - для компонентов-оберток

Такую реализацию можно даже попытаться усложнить, но в целом нет смысла - react-reconciler сразу поддерживает множество вещей

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

Более подробно про то, как написать свой рендерер можно послушать в докладе Софи Альперт youtube.com/watch?v=CGpMlW…

В докладе Ярослава Лосева @losyear на Tver.io youtube.com/watch?v=E1G2rM…

И на последнем @HolyJSconf, есть слайды holyjs-moscow.ru/2020/msk/talks…

Одним из самых серьезных челенджей при написании рендерера была реализация HMR - для ее реализации пришлось менять архитектуру

После перехода на react-reconciler схема выглядела так:
notion image

Мы запускали рендерер в мейн треде и создавали в методах хост-конфига ноды сразу, с помощью плагинного API фигмы figma.com/developers

Это отлично работало, тем что методы хост-конфига не могут быть асинхнронными, а апи фигмы - более-менее синхронно

За исключением HMR. Дело в том, что main-тред в Фигме надежно изолирован в песочнице и не позволяет использовать привычные браузерные апи (например, зафетчить какие-то данные из сети).

В фигма main трейд запускается с помощью движка QuickJS, написанном на C. Что само по себе создавало немало проблем - он хоть и поддерживает сразу es6, но падал банально на комментариях, слово "import" bellard.org/quickjs/

Написан на C, но ведь Фигма web-based, скажете вы. Так вот, они ранят этот движок с помощью WASM. Более подробно подробно об их плагинной песочнице можно почитать в статье figma.com/blog/how-we-bu…

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

Архитектура стала выглядеть примерно так, вместо того, чтобы создавать объекты непосредственно в методах хост конфига, мы стали посылать в сообщения в main-тред фигмы:
notion image

К счастью, есть реализация rpc для фигмы, которая частично облегчила эту задачу: github.com/Lona/figma-jso…

Но все равно, реакт реконсилер с промизами не работает, поэтому пришлось генерить айдишники, чтобы связывать ноды
notion image

Потому что реакт-реконсилер аппендидит за один проход, для этого по сгененрированным id находим ноды и соединяем их
notion image

Итоговый реквест выглядел примерно так github.com/react-figma/re…

Это же позволило достаточно легко реализовать поддержку React DevTools чуть позже github.com/react-figma/re…

На всякий слуйчай еще приведу такую картинку, main тред предназначен для работы с документом, UI - для того чтобы реализовать интерфейс плагина
notion image

Не мало хлопот также доставила поддержка стилизации с помощью Yoga Layout. Эта библиотека обеспечаивает поддержку лейаут-свойств - маржины, паддинги, флексбоксы yogalayout.com и используется, например в React Native

Поскольку нам было важно обеспечить совместимость по API с реакт нейтив, мы взяли именно ее react-figma.dev/docs/styling

Проблема была с тем, что Yoga не хотела запускаться внутри main-треда, пришлось ее загонять в UI-тред.

На вход - JSON-дерево c элементами и их стилями, на выходе абсолютные координаты, которые применяются к фигма-нодам

В Фигма постепенно развивается Auto-layout, все больше новых фич. Я думаю, постепенно мы сможем выпилить Yoga Layout, но пока еще не все свойства можно реализовать на авто-лейауте

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