Comments 193
Остаётся только один вопрос: если у вас есть информация о непосредственных зависимостях любого состояния, а значит вы точно знаете каке узлы в дереве нуждаются в перерисовке, то на кой чёрт вам сдался этот реакт с его перегенерацией виртуального дерева с последующей реконциляцией и поиском узлов, которые нуждаются в перерисовке?
Для всего этого виртуальный дом не нужен.
В первый проход я построил список, а при следующих изменениях данных что? Если я каждый раз буду список создавать заново на уровне дом-дерева, то это не производительно. Получается что мне нужно написать свои функции, которые будут способны рендерить только изменения. Но я больше чем уверен, что разнообразность задач, поставит перед фактами, что одним только этим не обойтись. И в итоге так и придется писать свой аналог реакта или чего-то подобного. Поэтому проще взять готовое и к тому же будет проще найти специалистов, которые точно в Вашем самописе не разбираются.
Да, вы всё правильно поняли. Это не просто возможно, но и гораздо эффективней. Изменилось что-то в сторе — перезапустилась функция обновления соответствующего участка дома.
Да, но если вас этих участков DOM изменилась тыща, то потребуется тыща обращений к DOM, а это — медленно.
React в этом случае всю тыщу изменений произведёт в виртуальном DOM, а в реальный сделает одну единственную вставку, что быстрее, т.к. в настоящий DOM будет всего одно обращение.
Да нет, Реакт сделает ровно столько же обращений к ДОМ, только ему для того, чтобы понять какие именно обращения к ДОМ необходимы — придётся делать сравнение виртуальных домов. Ну собственно, ваш пример с обновлением в 1000 местах — Ангуляр с точечными обновлениями уделывает Реакт в 2 раза.
Поможет ли здесь использование PureComponent?
Не поможет, ибо данные обновляются полностью.
Ок. Если данные обновляются полностью, то ReactJS перерендерит всё, от App и ниже в своём виртуальном DOM, а результат вставит на место #ROOT_APP сделав всего одно обращение к реальному DOM. Разве не так?
Не так, это было бы слишком медленно. Он пробежится по всем дом-узлам и проверит, чтобы их состояние соответствовало виртуальным дом-узлам.
Вы точно разбираетесь в ReactJS?
Но и вы не правы — перерисовывать все Реакт тоже не будет, иначе это приведет к побочным эффектам, о которых я писал.
Реакт просто сравнит старое и новое виртуальное дерево и применит изменения к DOM-дереву.
А вы сомневаетесь?
Теперь во всём. Похоже я живу в мире иллюзий, но и в этом я не уверен. Как собственно и в том, что vintage не потомок Сима.
Меня другое интересует. Как $mol умудряется в принципе обогнать Native DOM?
Очевидно за счёт ленивого рендеринга, который позволяет не только не рендерить то, что не изменилось, но и не рендерить то, что не видно.
К слову, клик по блоку в мол тормозит сильнее всего. Ангуляр работает практически с той же скоростью, что и нейтив, реакт — посередине.
Он далеко не "самый быстрый". Всё же ленивость и реактивность — не бесплатны. Но приложения на нём получаются самые отзывчивые. Именно потому, что он не делает то, что можно не делать. Но это синтетический бенчмарк. Вот более реалистичный.
К слову, клик по блоку в мол тормозит сильнее всего
Сомнительно. Может вы плавную анимацию фона посчитали за тормоза?
Да он вообще не быстрый.
Смотрите, этот тест направлен на то, чтобы оценить скорость рендеринга элементов фреймворком. Это предполагает, что элементы _будут_ рендериться. В результате можно оценить, как поведет себя фреймворк в ситуациях, которые сильно нагружают рендерер. Как ведет себя в таких ситуациях ваш фреймворк? Неизвестно, вы предпочли это скрыть.
> Но приложения на нём получаются самые отзывчивые.
Откуда это следует? Вы же тесты не хотите привести.
> Сомнительно. Может вы плавную анимацию фона посчитали за тормоза?
Не заметил никакой анимации.
> Но это синтетический бенчмарк. Вот более реалистичный.
Небось, там тоже все, что за пределами экрана — не рендерится, а по-этому оценить скорость работы мол нельзя?
Нет, он нацелен на то, чтобы оценить отзывчивость приложений. Пользователя не волнует сколько чего у вас будет рендериться. Более того, пользователь предпочтёт, если рендериться будет как можно меньше, а не будет впустую тратить батарейку его девайса. С точки зрения UX наиболее важная характеристика не "число отрендереных элементов в секунду", а "время между нажатием кнопки и отображением результата". Именно это и замеряется в вышеозначенных бенчмарках.
А чтобы ее измерить — надо понять скорость рендера. В итоге из вышеприведенных тестов мы можем понять, насколько отзывчив реакт или ангуляр, но не можем понять, насколько отзывчив $mol. Надо полагать — совершенно не отзывчив и очень силньо тормозит, иначе разработчик фреймворка не прилагал бы таких усилий, чтобы скрыть результаты тестов.
> С точки зрения UX наиболее важная характеристика не «число отрендереных элементов в секунду», а «время между нажатием кнопки и отображением результата». Именно это и замеряется в вышеозначенных бенчмарках.
Замечательно. Как мне узнать, какое время будет между нажатием кнопки и отражением результата в $mol, когда 10к блоков будут видны на экране?
Понимаете, случай с малым количеством блоков на экране никого не интересует — потому что он при ленивом рендеринге обрабатывается за время, неотличимое от мгновенного, на _всех_ фреймворках. И даже если ваш мол в данном случае дает задержку в 2мс вместо ангуляровских 5мс — какое кому до этого дело? Пользователь не увидит разницы.
А чтобы ее измерить — надо понять скорость рендера.
Чтобы её измерить надо взять приложение, кликнуть и измерить через сколько времени закончится рендеринг. Абстрактная "скорость рендеринга" — не более чем бесполезные попугайчики.
иначе разработчик фреймворка не прилагал бы таких усилий, чтобы скрыть результаты тестов
Нет никакого секрета. Можете уменьшить объём данных, чтобы они все попадали в видимую область — тогда будет рендериться всё и $mol окажется в самом конце. Но важно даже не это, а то, что не зависимо от объёмов данных задержка не превышает 200мс.
Как мне узнать, какое время будет между нажатием кнопки и отражением результата в $mol, когда 10к блоков будут видны на экране?
Давайте вы перестанете фантазировать про 10к видимых блоков. Я уже объяснил, почему это не реалистично.
при ленивом рендеринге обрабатывается за время, неотличимое от мгновенного, на всех фреймворках.
Какой ещё фреймворк умеет в ленивый рендеринг? Именно фреймворк. То, что ручками вы можете накостылять ленивость куда угодно — с этим никто не спорит. Но это требует дополнительных телодвижений, которых никто не делает пока не начинает припекать.
Там сейчас задержка на клик порядка секунды при 10к блоков, а вы хотите сказать, что он за 200мс все перерисует?
> Давайте вы перестанете фантазировать про 10к видимых блоков. Я уже объяснил, почему это не реалистично.
Давайте вы не будете объявлять вполне обычные кейзы «нереалистичными», а просто скажете, как поведет себя $mol в этом случае и какая будет задержка?
> То, что ручками вы можете накостылять ленивость куда угодно — с этим никто не спорит.
Так а что еще надо, учитывая, что ленивость эта нужна в паре-тройке мест во всем приложении (а большинстве случаев и вовсе не нужна никогда)? А вот быстрый рендеринг — нужен практически всегда.
Там сейчас задержка на клик порядка секунды при 10к блоков, а вы хотите сказать, что он за 200мс все перерисует?
Где "там"? Если вы про реализацию на $mol_dom_jsx, то там не используется ни реактивность, ни ленивость, ни реконциляция. Там дубовая реализация, аналогичная native dom. С той лишь разницей, что используется JSX для шалонизации.
Давайте вы не будете объявлять вполне обычные кейзы «нереалистичными», а просто скажете, как поведет себя $mol в этом случае и какая будет задержка?
Описание реалистичного кейса в студию.
ленивость эта нужна в паре-тройке мест во всем приложении (а большинстве случаев и вовсе не нужна никогда)?
Описание кейса, когда ленивость является лишней, в студию.
Я про обычный $mol.
> Описание кейса, когда ленивость является лишней, в студию.
Проще указать, когда является — если вам надо вывести какой-нибудь список на много-много позиций, и пагинация в данном конкретном случае плоха. Во всех остальных кейзах — ленивость является лишней и вредной.
> Описание реалистичного кейса в студию.
Я же вам указал — грид. Только не с пустыми ячейками, а с контентом.
Я про обычный $mol.
И как вы этого добились?
Во всех остальных кейзах — ленивость является лишней и вредной.
Обоснуйте.
Кликнул по блоку. Выше же говорил об этом уже, клики мол обрабатывает медленнее всех.
> Обоснуйте
Никакой пользы она не приносит, а накладные расходы — остаются.
Кликнул по блоку. Выше же говорил об этом уже, клики мол обрабатывает медленнее всех.
Не наблюдаю у себя ни в одном браузере. Поможете воспроизвести?
Никакой пользы она не приносит, а накладные расходы — остаются.
Высокая отзывчивость, плавные анимации, экономия батарейки — это бесполезные штуки?
Так у вас и без ленивой загрузки будет такая же высокая отзывчивость и плавные анимации. Единственное, где может быть выигрыш — это в плане батарейки, но это еще смотреть надо, сколько там выйдет.
> Не наблюдаю у себя ни в одном браузере. Поможете воспроизвести?
Первый приведенный вами бенчмарк, ставим 10к блоков, мотаем на середину, выделяем один блок, потом выделяем соседний, первый блок гаснет где-то через полсекунды-секунду. Никакой анимации при этом не видно (анимация есть при наведении на блок)
Так у вас и без ленивой загрузки будет такая же высокая отзывчивость и плавные анимации.
Ну конечно, делаем больше работы, а отзывчивость не проседает.
Первый приведенный вами бенчмарк, ставим 10к блоков, мотаем на середину, выделяем один блок, потом выделяем соседний, первый блок гаснет где-то через полсекунды-секунду.
Тут было две причины:
- Не кешировалось свойство row_selected, из-за чего все 10к элементов обновляли свои атрибуты при изменении текущего элемента. Поправил.
- Бага в хроме вызывает адские фризы при клике. Тоже поправил.
Теперь не тормозит. Спасибо :-)
Заодно обновил Реакт до 16 версии. Первичный рендеринг замедлился, зато обновление ускорилось.
Скажите, а ваш мега-ленивый $mol поисковыми роботами вообще индексируется? А Ctrl+F на ленивой странице работает?
Уже обсуждали. Вместо ctrl+f предлагается пользоваться специальным костыле-поиском на странице.
Для поисковых роботов всё нелениво рендерится на сервере.
Ctrl+F очевидно не найдёт то, что не отрендерено. Также он не найдёт то, в принципе не выводится (идентификаторы сущностей, например) или выводится в графическом формате (статусы, альтернативный текст картинок). И наоборот он найдёт кучу лишнего (например, каку-нибудь ерунду из рекламного блока в сайдбаре).
Можете провести юзабилити тестирование и посмотреть что выберут пользователи:
- Долгое открытие страницы, зато потом можно что-то найти по ctrl+f.
- Быстрое открытие страницы, но ctrl+f находит не всё.
- Быстрое открытие страницы, на котором есть поле фильтрации, которое позволяет отсеить нерелевантные данные.
- Долгое открытие страницы, на котором есть поле фильтрации, которое позволяет отсеить нерелевантные данные, но и можно поискать по ctrl+f.
Физическая реальность, к сожалению, такое не позволяет в общем случае.
- быстрый начальный рендер страницы (данные вьюпорта рендерятся сразу же, остальное отложенно)
- быстрый рендер по клику при открытии (т.к. нет ленивого рендера)
- поиск по Ctrl+F
- поле фильтрации
Ну только не говорите мне, что это невозможно реализовать.
Это более чем возможно. Но опять же:
- Пока не закончится рендеринг всего, ctrl+f не будет полноценно работать. А асинхронный рендеринг займёт куда больше времени, чем рендеринг сразу всего. Так что ctrl+f будет работать непредсказуемо для пользователя, что ещё хуже, чем просто работает / не работает.
- Пока идёт рендеринг того, что пользователь не видит — будут наблюдаться подтормаживания при работе с тем, что он видит. Поток всего один, а превысить 16мс — легко.
- Далеко не факт, что пользователь пойдёт потом мотать скролл до самого конца. В этом случае мы жрём батарейку впустую ради… возможности искать по ctrl+f?
- Чем больше элементов в ДОМ, тем медленнее происходит reflow, repaint и прочее.
- Чем больше мы нарендерили, тем больше потребуется видео-памяти (пропорционально площади всего контента в пикселях), которой на мобилках — кот наплакал.
3 — а вот для реализации этого юзкейза я участвовал в разработке подобного, но для html5 canvas, а не DOM. Так что, как видите, это не просто факт, а достаточно факт, чтобы превратиться в требование от стейкхолдера.
4 — для этого опять же существует vDOM. Например React.
5 — никуда не деться и я даже не знаю, что хуже: отсутствие привычных хоткеев или прожорливое приложение. 4-5 лет назад это было критическим пунктом, но не уверен, что это сейчас имеет смысл.
Пятый пункт — единственный, где ещё могут возникнуть вопросы. По остальным пунктам — это ответ на ваш самый первый комментарий к этой статье.
Про скорость repaint в DOM, мне кажется, Дмитрий имел в виду, что если на странице много элементов, в том числе и за пределами видимой области, то это будет тормозить. И vDOM тут никак не поможет.
vDOM не бесплатный, поэтому есть shouldComponentUpdate, отчасти setState, а также много попыток написать быстрее, чем в react (тот же inferno или ivi).
vDOM — это оптимизация внизу, перед выводом в браузер. А mobx-подобные решения — это оптимизация наверху, сразу после изменения данных. Что может быть гораздо быстрее любого vDOM, т.к. глубина вычислений будет меньше в среднем случае.
Многие фреймворки добиваются высокой производительности, используя точечные обновления, вместо vDOM (тот же angular4, отчасти vue). Интересно наблюдать, как развивается этот альтернативный подход, в том числе и в mol.
Если он имел ввиду это, то у нас юзкейзы с гридом, а браузеры уже научились оптимизировать paint и отбрасывать невидимые элементы. Слабо себе представляю пример, чтобы на экране были одновременно все дивы видимых грида, кроме выдуманного где-то здесь использования дивов в качестве пикселей.
vDOM не бесплатный, конечно же, но он быстрей, чем DOM. И его тоже надо уметь готовить. А чтобы использовать Inferno, всё равно надо соблюдать определённые соглашения и рекомендации. Итого получается то же самое, что и с ручной оптимизацией в узких местах. Доводилось слушать автора Inferno на конференции.
Мне тоже интересно, конечно же. Истоки этого обсуждения в том, что автор мола хочет объяснить, что React не нужен. Пока не объяснил)
Используя mobx, мы большую часть оптимизаций перекладываем с vDOM на mobx. Если эту идею довести до логического конца, то зачем тогда vDOM?.. А если не нужен vDOM, то некоторая часть реакта становится бесполезным оверхедом. Автор mol как раз это показал, написав небольшой наколеночный jsx-процессор на нативном DOM, сопоставимый с vDOM по производительности.
Рекомендации будут в случае любого фреймворка. Тут вопрос, не является ли часть из них следствием неудачных решений, принятых где-то раньше? Что было бы с рекомендациями, если бы перед реактом сделали mobx?
Вместо redux мы бы сразу получили mobx-state-tree с его децентрализованным стейтом и другими фичами.
shouldComponentUpdate стал бы не нужен, т.к. mobx решает эту проблему автоматически, не привнося сложности в прикладной код. Observer просто делает shallowEqual при любом обновлении данных.
componentWillMount стал бы не нужен, т.к. актуализацию данных можно делать в роутере или в самих данных (через fromPromise и lazyObservable из mobx-utils). О чем и говорит Michel Weststrate в статье How to decouple state and UI
Вопросом про нужность vDOM и оптимизации в MobX вы вернули всю дискуссию в самое начало и отправили по второму кругу. Так что давайте зайдём с другой стороны. Так вот, суть использования MobX — это обсёрваблы, подключённые к React-компонентам. После отправки данных в пропсы MobX никак не сможет повлиять на реконсиляцию. И выбросить реакт, этот ненужный оверхед с его реконсиляцией виртуального дома, можно только при следующих условиях:
- компоненты должны быть декомпозированы очень сильно. Чем меньше кирпичик — тем выше производительность. К чему и тяготеет мол.
- вы должны быть уверены, что в вашем проекте нет узких мест, которые зафризят UI. Сюда включается и начальный рендер (от чего мол убежал с мотивацией «так код будет более идиоматичный») и тяжёлые обновления (а от этого мол не смог убежать — фризы на демках появляются регулярно).
Иными словами, эта «заманчивая» идея подходит только для синтетических или в корне неверных бенчмарков или мелких приложений. Но я не хочу выделять каждый див в отдельный компонент, я не хочу, чтобы список товаров умирал через 5 секунд после начала прокрутки. Прозводительность этой «наколеночной» замены vDOM (как и исходная реализация мола) сравнима с React только при совсем тепличных условиях, любое реальное приложение выходит за рамки этих условий.
Что было бы с рекомендациями, если бы перед реактом сделали mobx?Подозреваю, что вместо реакта должно быть «ридакс»? По моему скромному мнению ничего не было бы, и до ридакса и после ридакса было много реализаций флюкса. В команде фейсбука достаточно много инженеров, радеющих за функциональный подход, чтобы продукты компании дрейфовали в сторону функциональщины. И Redux получил большее распространение не потому, что он появился раньше MobX, а потому, что он был скопирован с функционального Elm.
В целом библиотека MobX хорошая вещь, но она не конкурент Redux, они используют разные подходы, занимают разные ниши и по требованиям к продукту и по сообществу разработчиков.
И ещё пара замечаний:
shouldComponentUpdate и не нужен для того случая, который вы описали. Для этого в реакте давно есть ныне депрекейтед PureRenderMixin, который теперь React.PureComponent. Это не привносит сложности в прикладной код, просто вы наследуетесь от другого класса, и Pure… стоит использовать в 99% случаев. Для функциональных компонентов — pure HOC. Не знаю, в курсе ли вы, но в recompose есть и более вкусные вещи: onlyUpdateForKeys() и onlyUpdateForPropTypes(). ShouldComponentUpdate нужен в очень редких случаях. А когда нужен, он даёт ручное управление, что для производительности всегда будет лучше, чем умный change detection мобикса. Оптимизация рендера через обработку пропсов — это знания о преобразованиях внутри рендера, что явно ответственность компонента и должно быть рядом с компонентом. А не в сторонней библиотеке в виде универсального решения. Которое к тому же не даёт никакого выигрыша перед стандартным решением из реакта.
componentWillMount: статью эту впервые вижу. И знаете, я не использую componentWillMount)). И роутер у нас напрямую работает со стором (точнее с redux-saga). И реакт вообще там не причём, это не его зона ответственности, Мишель на пустом месте сам себе придумал проблему и сам решил её.
P.S. пример с моловским треугольником стал тормозить уже после 5-й открытой вкладки, в то время как демонстрация файбера так себя начинает вести только после 10-й или 12-й.
После отправки данных в пропсы MobX никак не сможет повлиять на реконсиляцию.Смысл в mobx, если отправлять сырые данные в пропсы. Вот то, что в случае mobx и react, в пропсы должны попадать запакованные значения, что б магия работала, скорее это ограничение.
компоненты должны быть декомпозированы очень сильно. Чем меньше кирпичик — тем выше производительность. К чему и тяготеет мол.Тут хотелось бы понять, какой пример мы обсуждаем: mol_jsx, mol или некий абстрактный на mobx и react без vdom на реальном dom.
Если я правильно вас понял, вы считаете, что все DOM-элементы в компоненте будут пересозданы, если какое-либо его observable-свойство поменялось.
Так реализация прослойки между mobx и DOM должна точечно мутировать DOM-ноду, если надо поменять одно из ее свойств или дочернюю ноду, как это и делает mol_jsx.
Наверное есть случаи, где такой подход будет работать хуже vDOM, но тут надо конкретный пример рассматривать и оценивать насколько это критично.
Для mol, лучше бы vintage объяснил, что будет, если делать все здоровенными компонентами.
вы должны быть уверены, что в вашем проекте нет узких мест, которые зафризят UI. Сюда включается и начальный рендер (от чего мол убежал с мотивацией «так код будет более идиоматичный») и тяжёлые обновления (а от этого мол не смог убежать — фризы на демках появляются регулярно).Тут не понял. Как vDOM может уберечь от фризов? Что такого было бы в приложении с mobx и нативном DOM, что фризило бы рендеринг?
Подозреваю, что вместо реакта должно быть «ридакс»?Именно реакта. Посыл был в том, что mobx делает многие вещи ненужными в реакте. Если бы проектировать начали с слоя данных, то дальше многие проблемы решать было бы проще. В Vue, кстати, в основе нечто похожее на mobx.
Почему не с redux, потому что это opinionated-подход, навязывающий определенный архитектурный стиль.
Mobx же претендует на unopinionated: обычные классы, минимум инфраструктурного кода. Нравится функциональный стиль — пожалуйста, mobx хорошая платформа для построения mst-подобных решений.
Можно было бы оставить только pure-компоненты, зоопарк способов менять состояние свелся бы к единообразному подходу на основе mobx и его надстройках. Все были бы счастливы.
shouldComponentUpdate и не нужен для того случая, который вы описалиЯ показал, что в mobx, observer его реализует одинаково для всех оборачиваемых pure-компонент.
Не знаю, в курсе ли вы, но в recompose есть и более вкусные вещи: onlyUpdateForKeys() и onlyUpdateForPropTypes(). ShouldComponentUpdate нужен в очень редких случаях.Изначальная идея — автоматически ускорить рендеринг и вдруг, для некоторых случаев, предлагается это делать вручную. Вопрос, где слабое звено в фреймворке, из-за которого возникла такая потребность? В vue как-то без shouldComponentUpdate обходятся.
Основной посыл был в том, что с mobx меньше инфраструктурного кода. Конечно со всякими redux-saga тоже меньше, но тут смысл в unopinionated, появляется выбор: можно напрямую в mobx состояние актуализировать, а можно и mst и saga-подобные решения накручивать.
P.S. пример с моловским треугольником стал тормозить уже после 5-й открытой вкладки, в то время как демонстрация файбераЯ к тому, что mol по сравнению с реактом без файбера выдавал лучшую производительность, сравнимую с реактом с файбером на одной вкладке, да.
Было б прикольно, если б vintage запилил свой файбер, но он наверное опять скажет «не нужно».
Да я пилил уже квантификацию вычислений, причём на уровне $mol_atom а не $mol_view. Но это даёт артефакты в духе "во второй половине страницы ничего не обновляется, когда в первой что-то постоянно обновляется" и тому подобные, так что пока отказался. Надо будет ещё подход сделать — может получится нормально реализовать.
Я к тому, что mol по сравнению с реактом без файбера выдавал лучшую производительность, сравнимую с реактом с файбером на одной вкладке, да.Это не так. Прочитайте вот этот комментарий Дмитрия. Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.
… автоматически ускорить рендеринг и вдруг, для некоторых случаев… В vue как-то без shouldComponentUpdate обходятся.Ну мы же выяснили, что shallowCompare (которое pure) есть и в реакте, мобиксовское тут ничего не добавляет. А обходятся потому что автор решил, что это не нужно. «Вам это не нужно, попробуйте организовать компоненты по-другому.» :) В моём текущем проекте всего два компонента имеют shouldComponentUpdate с кастомной функцией, остальным это не нужно. Два — это всё равно что ничего. Во Vue со второй версии тоже есть vDOM, и есть shallowCompare, но все равно есть проблемы с производительностью, к которым подойдёт ручная настройка.
Можно было бы оставить только pure-компоненты, зоопарк способов менять состояние свелся бы к единообразному подходу на основе mobx и его надстройках. Все были бы счастливы.Я счастлив, я меняю состояние компонентов через пропсы, и далеко не всё храню в глобальном сторе. И не использую зоопарк. А подход, который вы описали, на самом деле приводит к ещё одному конкурирующему стандарту.
Почему не с redux, потому что это opinionated-подход, навязывающий определенный архитектурный стиль.Не понял, почему? Вот у меня все компоненты берут всё из пропсов. Коннект — это просто один из внешних хоков (HOC). Они не знают ничего про ридакс и замечательно будут работать и с MobX и с реализацией флюкса на Backbone. И функциональные и обычные class-based компоненты.
Тут не понял. Как vDOM может уберечь от фризов? Что такого было бы в приложении с mobx и нативном DOM, что фризило бы рендеринг?Мы не совсем про vDOM говорим, а про ненужный реакт. Фризит начальный рендер, большое обновление данных за раз. Мол тут как раз замечательный пример: там используется ленивый рендер и отсечение элементов, не попадающих во вьюпорт. Если попытаться после рендера прокрутить вниз большой список, то появляются дикие фризы: мол пытается отрендерить сразу большое количество элементов (прокрутившихся мимо вьюпорта) за раз, да ещё и все вместе, никак их не кооперируя.
Если я правильно вас понял, вы считаете, что все DOM-элементы в компоненте будут пересозданы, если какое-либо его observable-свойство поменялось.Да, так, будут. Нет, не так, mol_jsx их пересоздаёт.
Так реализация прослойки между mobx и DOM должна точечно мутировать DOM-ноду, если надо поменять одно из ее свойств или дочернюю ноду, как это и делает mol_jsx.
Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.
Вы бы не говорили глупости, а проверили сперва сами. В этой демке $mol рендерит все кружочки, даже если они не влезают в экран.
Ох, давайте я расскажу вам как сейчас работает ленивый рендеринг в $mol_view:
- У каждого компонента есть свойство minimal_height. Рассчитывается оно эвристически на основе содержимого.
- Есть компонент, который управляет скроллингом — $mol_scroll — он предоставляет информацию о нижнем крае видимой области.
- Есть компоненты-лейауты. Например, используемый в "демке на прокрутку" вертикальный лейаут — $mol_list. Он умеет использовать информацию о минимальном размере вложенных в него компонент и нижнюю границу скроллинга, чтобы отсечь те компоненты, которые точно не попадают в видимую область. Так как minimal_height как правило не соответствует реальным размерам компонент (они больше), то $mol_list обычно рендерит чуть дальше видимой области.
В треугольнике Серпинского ничего ленивого не используется, ибо предполагается его запускать так, чтобы он полностью влезал в видимую область.
вы обманываете пользователей не только тем, что неправильно меряете отзывчивость (как — уже указывал), но и неправильным текстом
Не обманываем. Там замеряются 2 основных параметра, важных для любой страницы — время показа и время обновления данных. Всё это с точки зрения пользователя.
Потому что используете производный компонент для своего мол, а в остальных случаях — дубовая реализация.
Во всех реализациях написан идиоматичный для фреймворка код.
Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.Пока не понял что модифицировать, вроде столько же кружочков, у меня не тормозит.
Во Vue со второй версии тоже есть vDOM, и есть shallowCompare, но все равно есть проблемы с производительностью, к которым подойдёт ручная настройка.Чем меньше деталей о реакте приложение знает, тем лучше, зачем лишний инфраструктурный код. По мне, лучше сперва научить фреймворк проблему решать автоматически, только когда уже все способы исчерпаны, дать еще один способ выстрелить в ногу, ручной оптимизацией. Возможно автор vue считает также.
Я счастлив, я меняю состояние компонентов через пропсы, и далеко не всё храню в глобальном сторе. И не использую зоопаркПропсы никто не отменяет. Отказ от сырых данных в пропсах для всех компонент это неудобно, да. В реакте пока мы видим компромисс, когда только некоторые компоненты являются observables, а дальше задача ложится на чистые компоненты и vDOM. Но это из-за того, что реакт изначально не предполагалось использовать с такими структурами данных. Vue, angular и пресловутый mol как раз более приспособлены для них, хотя в первом пока тоже vDOM на edge-cases используется.
Зоопарк бывает не только библиотек, но и идей, когда нет единообразия в смыслах и одной ответственности. Кроме чистых компонент вы используете HOC-и и компоненты с setState, если не все храните все в глобальном сторе. По мне, так уже зоопарк из 3х типов компонентов, появившихся из-за компромиссов, на которые пошли, когда менять основу было уже поздно.
Чистые кастомизируются хорошо, HOC плохо, т.к. к предоставляют биндинги к стейту на редаксе и прибиты к бизнес логике, с setState тоже самое, но без редакса и стейт с логикой уже нельзя отделить от компонента и от реакта. Понятие компонент вообще размыто. Есть presentational компоненты (View), container-компоненты с данными и логикой (Model), компоненты-контроллеры (react-router, react-helmet), компоненты-биндинги (HOC).
Mobx же позволяет больше компонентов сделать в единообразном стиле хотя бы. Почти везде будет чистый компонент с observable оберткой. Будет логика отделена от верстки, что лучше для SRP.
А подход, который вы описали, на самом деле приводит к ещё одному конкурирующему стандарту.Я имел в виду, что если бы mobx был в основе react, то многое упростилось. Как раз меньше было бы стандартов и дублирующих функциональность библиотек, т.к. более мощное решение в основе. Да и потом, вроде на фронтенде нормально, что кто-то всегда будет искать лучшее решение. Судя по этой активности, приблизиться к идеалу ни у кого пока не получилось.
Не понял, почему? Вот у меня все компоненты берут всё из пропсов.Компоненты — только часть приложения. А как же всякие actions, reducers, middlewares — это все opionated, т.к. навязывает определенный стиль, функциональный подход, свои соглашения по модификации состояния и т.д. У mobx эти соглашения проще и ближе к нативному js, в статье об этом говорится, причем на этой абстракции можно строить сложные, вроде mst.
Да, так, будут. Нет, не так, mol_jsx их пересоздаёт.Не пойму, почему вы так решили. В том комменте говорится, будут пересозданы, если id-шника нет. Судя по реализации там есть переспользование и в примере видно, что не пересоздается нода. Может мы о разных нодах говорим?
Пока не понял что модифицировать, вроде столько же кружочков, у меня не тормозит.Я смотрел на оригинальную демонстрацию, а не на его, и вот там нет скейла .75 и на небольшом 17" экране ноутбука не полностью влезает. Дмитрий уже рассказал, что в этой демке используется по-другому mol_view, не обёрнутый в контроллер, предоставляющий вьюпорт :) Так что вопрос про модификацию отпал.
Чем меньше деталей о реакте приложение знает, тем лучше, зачем лишний инфраструктурный код. По мне, лучше сперва научить фреймворк проблему решать автоматически, только когда уже все способы исчерпаны, дать еще один способ выстрелить в ногу, ручной оптимизацией..Знаете, а я тоже так думаю :) И вот по-моему как раз реакт — лучший пример такого подхода. Компоненты принимают просто параметры извне, а всю работу поручают собственно реакту. Но они не забыли про ручную оптимизацию) А вот нет, автор Vue как раз так не считает (вы читали issue по ссылке?). Он прямо говорит, что если ваш компонент слишком часто перерисовывается, то просто разбейте его. Конечно же тогда shallowCompare разобьётся на столько отдельных функций, сколько компонентов будет создано, каждая на вход будет принимать меньше изменившихся пропсов. Это та же ручная оптимизация, но в ущерб читабельности и в ущерб поддержке кода. Куда логичней вынести это в отдельную функцию, правильно? Которую по-хорошему можно и переиспользовать (экстракт в отдельный файл и использование как хок). Вот ещё прямой ответ, что не нужен shouldComponentUpdate… потому что не нужен.
Кроме чистых компонент вы используете HOC-и и компоненты с setState, если не все храните все в глобальном сторе.Здесь и далее много неточностей. Мне нравится реакт, но не нравятся многие подходы, принятые в мейнстриме реакт-сообщества. У нас нет разделения на тупые и умные компоненты, грубо говоря они все умные (использование каких-то тупых промежуточных компонентов, которые будут трансферить пропсы в дерево нижележащихся компонентов считаю абсолютно неверным подходом, напрочь убивающим какую-либо возможность реюза и усложняющим модификацию UI). В целом подход к организации одного компонента очень близок к Vue с его однофайловыми компонентами, только у нас однопапочные компоненты.
У нас нет setState, только хоки и ридакс, в сторе лежит минимум данных — только те, которые нужны для апи и некоторые глобальные производные. Кстати, какие же хоки биндинги? Это же просто high orders functions. У нас нет трёх типов компонентов — всё просто функциональные stateless компоненты. react-router считаю громадной ошибкой. react-helmet — это чудо тоже используем, но какой же это контроллер? Он работает так же, как сборка стилей для критического CSS при SSR.
В итоге: всё является чистыми функциями (насколько это возможно в js). Компоненты (написанные в едином стиле), хоки, селекторы, actionCreator'ы и редьюсеры — всё чистые функции. Сайд эффекты в сагах. Это всё неприлично просто покрывается юнит-тестами, упрощает модификацию по сравнению с классическим построением реакта.
Возможно я нахожусь в каком-то локальном экстремуме, но я совершенно не вижу, как можно большой объём переиспользуемой логики (продукт — личный кабинет, высокодинамичный SPA с SSR) представить в виде вороха компьютед и обсёрваблов. И получить те же компоненты в едином стиле, которые сейчас у нас есть.
Я имел в виду, что если бы mobx был в основе react, то многое упростилось. Как раз меньше было бы стандартов и дублирующих функциональность библиотек, т.к. более мощное решение в основе.Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?
Сам Мишель пишет, что Mobx в этом плане ближе к Ember и Knockout, обе либы набрали популярность до появления реакта. Обе предоставляют те же самые обсёрваблы и компьютед. Как видим, все эти условия существовали уже (чтобы подобная реализация была в основе реакт). Почему же реакт был сделан на чистом листе?
Компоненты — только часть приложения. А как же всякие actions, reducers, middlewares — это все opionated, т.к. навязывает определенный стиль, функциональный подход, свои соглашения по модификации состояния и т.д. У mobx эти соглашения проще и ближе к нативному js, в статье об этом говорится, причем на этой абстракции можно строить сложные, вроде mst.Насчёт ближе к нативному js — ой как спорно, ведь что может быть проще, чем просто функции и просто объекты. Наверно вы имели ввиду, что больше свободы.
Не пойму, почему вы так решили. В том комменте говорится, будут пересозданы, если id-шника нет. Судя по реализации там есть переспользование и в примере видно, что не пересоздается нода. Может мы о разных нодах говорим?Верно, id-шника нет — значит пересоздаём, потому что неизвестно какая нода: без явной ручной привязки выяснить это можно только сравнением нового и старого vDOM. Ничего такого в mol_jsx нет, там вообще нет реконсиляции. Это не виртуальный дом, а просто jsx-адаптер, о чём честно и написано.
Вот ещё прямой ответ, что не нужен shouldComponentUpdate… потому что не нужен.
Не нужен. Там человек пытался вырывать гланды через анус — ему объяснили, что так делать не правильно. Ему надо было чтобы содержимое не обновлялось, пока его редактируют, а shouldComponentUpdate вырубил бы вообще все обновления (даже атрибутов и свойств).
Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?
А где хранится стейт компонент?
> Почему же реакт был сделан на чистом листе?
Потому что стояла задача портирования существующего пхп кода со вполне определенной архитектурой (принимаем запрос с параметрами — рендерим по этим параметрам страницу). На обсерваблы и компьютеды это не ложится, зато ложится на чистые компоненты.
> Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?
В том, что обсерваблы в ангуляре для апдейта дом не используются, так что нет, не «в ангуляре те же самые обсерваблы».
А где хранится стейт компонент?Используем recompose, выносящий хранение стейта в хоки. Благодаря этому компонент просто получает пропсы и не знает, откуда они приходят.
Потому что стояла задача портирования существующего пхп кода со вполне определенной архитектурой (принимаем запрос с параметрами — рендерим по этим параметрам страницу). На обсерваблы и компьютеды это не ложится, зато ложится на чистые компоненты.Это был вопрос, на который я не ждал ответа) Моя цель была показать, что Mobx-подобные подходы уже были, и что они не повлияли на реакт (а может и повлияли, но в обратном ключе: показали, как не надо делать).
А про саму суть вопроса — я читал немного другое: что цель стояла как раз сделать подход, подобный XHP (автоматическая обработка xml-like синтаксиса для безопасности), для работы с динамическими приложениями в браузере. И что на реакт повлиял Clojure и создание Om как обёртки реакта (на CloseScript), что привело и к зависимости реконсиляции от иммутабельных структур.
Т.е. дело не в наследии от PHP, а в наличии адептов функционального программирования.
То есть внутренный компонент может менять внешний стейит из НОС?
> Моя цель была показать, что Mobx-подобные подходы уже были, и что они не повлияли на реакт (а может и повлияли, но в обратном ключе: показали, как не надо делать).
Они на него не повлияли потому что были неприменимы в одном конкретном случае, для которого разрабатывался реакт.
> Т.е. дело не в наследии от PHP,
Прямо по вашей ссылке:
> ReactJS started as a JavaScript port of XHP, a version of PHP which Facebook released four years ago.
> а в наличии адептов функционального программирования.
Уже это обсуждалось. В реакте функционального программирования не больше, чем в любом другом фреймворке. Ну и вообще странно ожидать от кондовых пехепешников из фейсбука любви к функциональным подходам :)
Но они не забыли про ручную оптимизацию)Это как повернуть. На мой взгляд это следствие того, что vDOM не спасал в сложных компонентах и на больших глубинах вложенности. Естественно никто не скажет, мы такие растяпы, не учли это сразу, поэтому простите нас, вот хотя бы shouldComponentUpdate, в ангуларе с его update strategies еще похлеще.
Это та же ручная оптимизация, но в ущерб читабельности и в ущерб поддержке кода.Не та же, shouldComponentUpdate — дополнительный код, который не решает бизнес задачу, а лишь оптимизирует ее время выполнения, при этом может содержать ошибки и должен быть покрыт тестами. Лучший код — который не написан. shouldComponentUpdate это когда компонент верхнего уровня начинает решать за компоненты нижнего уровня рендериться им или нет, не имея понятия об их особенностях реализации. Это например больно бьет, если дочерние используют контекст, наверху он обновился, но shouldComponentUpdate в родителе эти обновления не донес дочерних, т.к. родитель не учел контекст. Контексты вообще ахиллесова пята в реакте.
Из того issue в vue:
The fully framework-compliant way to deal with it (and is also what I would suggest even in React) is to have a separate piece of state that represents the input content, which can then be out of sync with the model state and can be updated only when you want to.Я вот согласен с этим и это вовсе не «не нужен shouldComponentUpdate… потому что не нужен». Если выбирать между shouldComponentUpdate и более детальным разбиением на компоненты, лучше последнее.
Здесь и далее много неточностей.Много, что б было меньше, не могли бы вы привести пример, как вы готовите редакс с сагой? Если на 5м уровне вложенности вам надо галочку на интерфейсе отобразить, тоже через редакс делаете или клепаете экшен/редьюссер? У вас один центральный стор или много инстансов редакса в приложении? Иерархия модулей плоская или фрактальная? Кто делает bootstrap начальных данных саги очередного компонента мини-приложения? Как решается, какой компонент обмазывать HOC-ами и сагами, а какой использовать чистым? Как достигается типобезопасность в HOC, что стейт пришедший в connect, имеет свойство blaBla?
Мне нравится реакт, но не нравятся многие подходы, принятые в мейнстриме реакт-сообществаВот-вот, реакт отличается множеством способов выстрелить в ногу, т.к. основа не дает цельного, масштабируемого инструмента и спецификации для комфортной разработки. Поэтому в экосистеме столько решений с дублирующейся функциональностью. Дело конечно еще и в громадном накопленном опыте решений, ради сохранения совместимости костыли неизбежны, неизвестно что еще в дальнейшем произойдет с vue и т.д. Мне интересно, кто оказажется прозорливее и угадает лучше. Вроде vue пока многие вещи пока лучше угадывали.
react-helmet. Суть в том, что на компонентах пытаются программировать не только верстку. Все эти конструкции в верстке, нарушают разделение ответственности, изоляцию и размывают границы между слоями. Уже непонятно, что верстка, а что управляющая конструкция. react-helmet — ничего не выводит в компонент, он сайд-эффект, меняющий title, поэтому я и назвал его контроллером. Эта хрень не должна быть в компонентах. Все это напоминает олдскул php, с его запросами к базе посреди шаблона, только замаскированный под красивый xml-синтаксис.
Кстати, какие же хоки биндинги? Это же просто high orders functions. У нас нет трёх типов компонентов — всё просто функциональные stateless компоненты.Ох, тут много можно сказать, раз уж пошла речь о HOC. HOC как раз и будет трансферить пропсы в дерево нижележащихся компонентов. Поэтому я и говорил биндинги, намекая на его функцию. Название high orders functions вводит в заблуждение, т.к. это не единственный способ делать связывание компонент с логикой, а задача то именно эта решается.
В ангуларе например, есть нативный конструктор класса, типы и DI для этих же целей. При этом HOC редакса рождает намертво прибитый к редаксу, путям в стейте и экшенам компонент и в этом заключается его непереиспользуемость, нарушается OCP: нельзя расширить компонент без рефакторинга.
Сага с редаксом, правда, дает возможность большую часть логики убрать из HOC, сделав его очень тонким. Правда при этом нарушается принцип инверсии контроля, когда зависимости верхнего уровня (точка входа) знают о деталях нижних, в виде редьюсеров и саг. И это не избавляет от шаблонного кода для связывания, вроде dispatch(bla-bla). Инверсия контроля же позволяет декларативно на любом уровне иерархии переопределять эту связь и для любого компонента вообще, у винтажа похоже, кстати, с его контекстами.
Еще HOC усложняют вывод типов, т.к. всякие connect могут по сложной схеме менять контракт компонентов. Маппинг реальных пропсов в то, что получается на выходе connect не однозначен. Это может быть поправят, т.к. в flow, наряду с костылями «спешал фор реакт», добавили костылей «спешал фор редакс», вроде $Diff или $ObjMap. Правда пока в flow-typed их почему-то не используют. Это не говоря о таких вещах, как вывод типов state в mapStateToProps, что вообще не представляется возможным с текущим flow.
function A(props: {a: number, b: string, onClick: () => void}) { return null }
const mapStateToProps = (state, props) => {
return {
active: props.c + props.a
}
}
const mapDispatchToProps = (dispatch, props) => {
return {
onClick: () => {
dispatch({some: props.filter})
}
}
}
const AHoc = connect(mapStateToProps, mapDispatchToProps)(A)
Возможно я нахожусь в каком-то локальном экстремуме, но я совершенно не вижу, как можно большой объём переиспользуемой логики (продукт — личный кабинет, высокодинамичный SPA с SSR) представить в виде вороха компьютед и обсёрвабловЕсли обсервабл выглядит как обычный класс с методами и свойствами, зависимости которого в конструктор инжектятся, то тестировать и мокать легко. Это кому что удобнее, вечный холивор ООП vs ФП. Никто не лучше, инструментов и там и там достаточно. У меня другая жизненная школа, я за ООП, SOLID и т.д, поэтому экспериментирую с этим в реакт, т.к. уверен, что результат будет понятнее и проще ООП-шникам.
Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?О, не те же самые. Все дело в акцентах. Попробую систематизировать сорта «обсерваблов»:
RxJS, beacon, kefir, most, pull-stream, ramda, отчасти es observable — разной степени удачности и монстроидальности попытки сделать ФП в js. Отличаются навязчивым API. Т.е. приложения на них — это вкрапления нативного синхронного кода в апи этих библиотек. Вы выстраиваете поток простых вычислений, вставляя их в огромное кол-во точек расширения библиотеки или заменяете все операции на функции. Грубо c = add(a, b) вместо c = a + b. Для ООП-шников это выглядит как слишком много мусора и, в целом, сложно для освоения. Лично я считаю, если в языке нет из коробки средств для ФП, то не надо их туда тащить, это не в философии языка.
Baobab и д.р. переходные формы, популярные пару лет назад решения на основе single state observable дерева, концепция проще чем у стримов, т.к. в основном есть только источник observable и производная computable.
mobx — дальнейшее упрощение концепции стримов, когда стримы замаскировали под нативные классы и объекты, избавив ООП-шников, коих большинство, от изучения новых концепций и дав свободу от архитектурных стилей, можно самому лепить из классов что угодно. Фишка mobx в автоматическом связывании частей вычисления, когда вы пишите c = a + b и этого достаточно, что б при обновлении a, обновился и c. Т.к. это упрощение, многие, но не все задачи на нем красиво и просто решаются, поэтому Мишель в своем в своем faq и написал When to use RxJS instead of MobX.
mol_atom от vintage, cellx от Riim и lom_atom моя имплементация атомов Дмитрия для реакта — Еще более упрощенный mobx, есть только одна сущность — атом. Причем данные сами синхронизируют себя с асинхронным стором по факту обращения к ним. Это ключевой момент, отличающий их от всего, что мне известно. То, с чем vintage носится столько лет и говорит как про pull, вытягивание. Напоминает идею инверсии контроля, только для observable-данных.
Используете вы RxJS, mobx (хотя и в нем можно через хелперы, но как я выше говорил, все дело в акцентах), редакс с сагой или еще что, ответственность по актуализации лежит за потребителем данных, componentDidMount, bootstrap саги с ручным явным вызовом или не важно как там это замаскировать.
Нарушается инкапсуляция и SRP, т.к. факт необходимости загрузки — это деталь, которая просачивается в вышележащий уровень. В сложных приложениях, при рефакторинге нижнего уровня, неизбежно надо рефачить верхний, что черевато багами. Это винтаж имеет в виду, когда говорит про багоемкость.
То, что технология не популярна, может также означать, что у автора нет соотвествующих качеств/ресурсов для ее популяризации. Первоначальную идею я встретил у винтажа в его jin.atom в 2014 году и никакого mobx-а тогда небыло. Идея c = a + b теперь такая же в mobx. Ноосфера, она такая, тут Мишель верно угадал и смог продать.
Насчёт ближе к нативному js — ой как спорно, ведь что может быть проще, чем просто функции и просто объектыСпорно, так спорно. Это не просто функции и объекты. Важен смысл, который за ними, в mobx этих смысловых сущностей меньше и они выглядят проще, иными словами более простой core concept, который ближе к нативным классам. user.name = 'test' вместо dispatch({type: 'CHANGE_USER_NAME', name: 'test'})
Вообще, я имел в виду приведенную вами статью Мишеля:
Probably, it is more accurate to talk about MobX as a data flow library, that enables you to roll your own state management architecture with minimal effort.
Верно, id-шника нет — значит пересоздаём, потому что неизвестно какая нодаВаше заявление было категорично: «Нет, не так, mol_jsx их пересоздаёт.». Это не так, если id-шник есть. По мне, малая плата для достижения эффекта точечного обновления DOM без vDOM и реконсиляции. Вот то, что JSX для идентифкации кодовых блоков подходит хреново и совсем без ручной расстановки id-шников не обойтись, это другой вопрос. В идеале бы свойство языка таким сделать. Кроме этого, идентификация помогает идеально следовать принципу open/close во всем приложении, чего пока нет нигде, кроме mol. Вот поэтому vintage и выдумал свой tree и пытается его продать. В этом есть рациональное зерно.
Много, что б было меньше, не могли бы вы привести пример, как вы готовите редакс с сагой? Если на 5м уровне вложенности вам надо галочку на интерфейсе отобразить, тоже через редакс делаете или клепаете экшен/редьюссер? У вас один центральный стор или много инстансов редакса в приложении? Иерархия модулей плоская или фрактальная? Кто делает bootstrap начальных данных саги очередного компонента мини-приложения? Как решается, какой компонент обмазывать HOC-ами и сагами, а какой использовать чистым? Как достигается типобезопасность в HOC, что стейт пришедший в connect, имеет свойство blaBla?
Где именно хранить состояние галочки — это вопрос насколько эта галочка должна быть доступна в других частях приложения и должно ли это состояние синхрониться с сервером. Не очень понял разницу «редакс или экшен/редьюсер». Стор один. Если галочка локальная и влияет только на доступность какого-то соседнего блока, то делается хок (пусть хок#1) сразу для компонента 5-го уровня, который хранит состояние этой галочки и передаёт его как пропс. Всё, галочка рисуется там, где надо.
Предположим галочка теперь приходит из апишки и кладётся в стор сагой, тогда для конкретной вьюшки обновляется селектор (чистая функция), который знает о нужных ему данных и отдаёт их в новый хок#2 (который connect от react-redux) сразу к этому компоненту на 5-ом уровне (вместо хок#1). Этот хок#2 не изменится, если затем будет меняться апишка или структура данных, меняется только селектор. Всё, галочка рисуется там, где надо. Файл с чистым компонентом затронут не был.
Если под иерархией модулей подразумевается структура папок, то сейчас она фрактальная. Плоская не подходит из-за слишком большого количества компонентов.
Совсем не понял про бутстрап данных саги, у нас нет мини-приложений, постоянно работает разное количество саг. Компоненты все чистые и все обмазаны хоками и выглядит это примерно вот так. Только у нас энхансеры (презентационная логика) и сам компонент (только вёрстка) лежат отдельно, в соседних файлах.
Ни компоненты ни энхансеры не знают ничего про саги вообще, они знают только про пропсы. Компоненты в свою очередь не знают ничего про энхансеры, а только про пропсы. Саги ничего не знают про компоненты, они меняют состояние стора только через actions, а при необходимости берут состояние через те же чистые селекторы, которые выше уже указывал (в примере про галочку). Ничего про структуру стора сага не знает, при изменении структуры обновить надо будет только редьюсеры и селекторы. Все соучастники преступления, описанного выше — чистые функции (кроме саг, конечно же).
Типобезопасность в самих хоках сейчас пока никак не решается, это происходит только в селекторах и в компонентах (flow-аннотации для пропсов всех компонентов). Но connect-хоки (которых порядка 5%-10% от всех хоков) состоят только из селекторов. Общие переиспользуемые хоки не типизированы (в беклоге задача).
react-helmet. Суть в том, что...Понял вас) Да, мне тоже не нравится, когда jsx пытаются использовать как управляющие конструкции, это меня как раз сильно отталкивает от react-router. У нас helmet используется только в одном месте для удобного вывода в head, чтобы работать с jsx, а не со строками в dangerouslySetInnerHTML. Все сайд-эффекты через саги.
При этом HOC редакса рождает намертво прибитый к редаксу, путям в стейте и экшенам компонент и в этом заключается его непереиспользуемость, нарушается OCP: нельзя расширить компонент без рефакторинга.К путям, допустим, не прибиты, а в остальном да, закрытый для расширения наследованием получается. Открытый только для композиции.
Сага с редаксом, правда, дает возможность большую часть логики убрать из HOC, сделав его очень тонким. Правда при этом нарушается принцип инверсии контроля, когда зависимости верхнего уровня (точка входа) знают о деталях нижних, в виде редьюсеров и саг.Не очень понял, какие точки входа знают про саги. Сага вещь в себе, она работает только со стором, через стандартные actions и стандартные selectors (эти же экшены и селекторы используются для компонентов).
Ваше заявление было категорично: «Нет, не так, mol_jsx их пересоздаёт.»На самом деле мы изначально говорили про работу MobX-подобных решений без реакта. И про размер вёрстки в каждом компоненте. Не помню, скидывал ли я тогда, но Мишель сам прямо говорит: дробите компоненты как можно меньше, чтобы меньше перерендеривать. И то же самое справедливо для mol_jsx: обычный компонент будет полностью перерендериваться.
Хорошо, если там есть поддержка id-шников, то вы готовы в поддерживаемых приложениях для каждого дива проставить айдишник?
P.S. И вообще мы далеко отошли от основного вопроса — бессмысленности реакта при наличии mobx. Предлагаю либо закругляться, либо в личку перейти)
Не очень понял разницу «редакс или экшен/редьюсер».Да, я ошибся, имелось в виду setState или экшен/редьюссер. Но теперь я понял про recompose с withState. Для меня важен подход, когда есть прозрачность интерфейсов, везде и точно работает выведение типов без костылей вроде utility types. Когда типизация помогает, а не мешает, когда есть единообразие и простота реализации SOLID (хотя бы SO) — это индикатор того, что путь верный. Как только появляются строки для работы со стейтом, возникают затруднение с выведением — это плохой признак.
По мне, даже setState был бы лучше этого. Напомитает подход, который был в React.createClass, описание класса через фабрику, нестандартную спецификацию библиотеки.
Даже если забить на выведение типов, которое тут никогда не заработает, вы действительно считаете, что вот это
const enhance = compose(
renameProp('imageUrl', 'thumbnailUrl'),
withState('name', 'setName', props => props.name),
withState('favourites', 'setFavourites', []),
withState('favouriteText', 'setFavouriteText', ''),
withHandlers({
updateName: ({ setName }) => event => {
setName(event.target.value);
},
updateFavouriteText: ({ setFavouriteText }) => event => {
setFavouriteText(event.target.value);
},
addListEntry: ({
setFavourites,
favourites,
favouriteText,
setFavouriteText
}) => event => {
event.preventDefault();
setFavourites([favouriteText, ...favourites]);
setFavouriteText('');
}
})
);
const FavoritesExt = enhance(Favourites);
Проще и понятнее, чем это?
class FavoritesExt extends Component {
constructor(props, context) {
super(props, context)
this.state = {
name: props.name,
favourites: [],
favouriteText: ''
}
}
updateName = ({target}) => { this.setState({name: target.value}) }
updateFavouriteText = ({target}) => { this.setState({favouriteText: target.value}) }
addListEntry = (e) => {
e.preventDefault()
this.setState({
favourites: [this.state.favouriteText, ...this.state.favourites],
favouriteText: ''
})
}
render() {
return Favorites({
...this.props,
thumbnailUrl: this.props.imageUrl,
...this.state,
addListEntry: this.addListEntry,
updateName: this.updateName,
updateFavouriteText: this.updateFavouriteText
})
}
}
Если уж пытаться добавлять сахар, то по мне лучше так:
class FavoritesService {
@mem favouriteText = ''
@mem favourites: string[] = []
@mem name = ''
@props set props({name}: {name: string}) {
this.name = name
}
addListEntry = (e: Event) => {
e.preventDefault()
this.favourites = [...this.favoriteText, this.favourites]
this.favoriteText = ''
}
updateName = ({target}: Event) => { this.name = target.value }
updateFavouriteText = ({target}: Event) => { this.favouriteText = target.value }
}
function FavoritesHoc(_: {name: string}, service: FavoritesService) {
return Favorites({...service})
}
Заметьте, последний пример вообще от реакта не зависит, только от JSX и HOC-ов там в принципе нет. compose же зависит, в нем внутри обычный container-компонент на реакте.
то делается хок (пусть хок#1) сразу для компонента 5-го уровня, который хранит состояние этой галочки и передаёт его как пропс.HOC — это всегда привязка к конкретной реализации чего-то. В реакте выбора особо нет. Если мало HOC — куча пропсов тянутся до ближайшего HOC, повышая сложность рефакторинга. Много HOC — возрастает сложность переиспользования и появляется копипаст, когда берут тот же чистый компонент и лепят поверх новый HOC. Завязки на реализации проникают на все уровни иерархии приложения.
Предположим галочка теперь приходит из апишки и кладётся в стор сагой, тогда для конкретной вьюшки обновляется селектор (чистая функция)Возможна только модификация существующего кода, вместо расширения. Вот если бы так работало Хок2(Хок1(Компонент)), как при наследовании. Получается следуем одному принципу в ущерб другому (либо хок, либо возможность расширения), подобная дихотомия много где в реакте встречается.
Файл с чистым компонентом затронут не был.Есть же не только компоненты. Все, что вокруг получается не переиспользовать. Нужен компонент с таким же поведением, но с другой копией стейта — придется копипастить. Проблема в отсутствии единообразия с сохранением расширямости в любых частях приложения, которая нужна для фрактальной масштабируемости.
Не очень понял, какие точки входа знают про саги. Сага вещь в себе, она работает только со стором, через стандартные actions и стандартные selectors (эти же экшены и селекторы используются для компонентов).У вас же центральный стейт, так? Где-то в index.js есть место, где регистрируются все редьюссеры, все саги. Это — детали реализации конкретных частей приложения, утекающие в index.js. По какому событию вы загружаете начальные данные очередного компонента в процессе работы с приложением, где bootstrap происходит? А точно это должно быть в том месте и в то время?
Хорошо, если там есть поддержка id-шников, то вы готовы в поддерживаемых приложениях для каждого дива проставить айдишник?mol_jsx proof of concept. Я ж написал, что JSX плохой инструмент для идентификации кодовых блоков. Да и много ньюансов есть, не везде надо их проставлять, только если в компоненте ветвления и т.п. Да и так ли страшно это пересоздание, это ж не во всех кейсах происходит. В choo например, на это забили, ну да в тестах он не блещет, но вполне работоспособен для нетяжелых данных. Есть гораздо более легковесные real dom diff алгоритмы, например nanomorph. У винтажа в mol свой diff, с блекджеком.
P.S. И вообще мы далеко отошли от основного вопроса — бессмысленности реакта при наличии mobxЕсли развивать идею mobx и под нее делать экосистему, то да, vdom не нужным становится, а diff алгоритм сильно упрощается. Тот вброс Дмитрия в любом случае приведет к обсуждению целого ряда недостатков реакта и экосистемы вокруг него, это системная проблема. Я думаю, со временем архитектурная критика будет только нарастать, например как в «React-стек: скрытые угрозы», Илья Климов
.
Предлагаю либо закругляться, либо в личку перейти)Да, давайте прервемся и как-нибудь в другой раз продолжим. Слишком много направлений, в комментах тяжко это обсуждать.
я не хочу выделять каждый див в отдельный компонент
Забавно, что именно в Реакте так в основном и делают.
А что вас смущает в том, что каждый див — отдельный компонент? $mol_view компонент — не более чем легковесная реактивная обёртка над дом-элементом.
я не хочу, чтобы список товаров умирал через 5 секунд после начала прокрутки.
Вы хотите, чтобы он умирал сразу, если программист не позаботился о паджинации?
Прозводительность этой «наколеночной» замены vDOM (как и исходная реализация мола) сравнима с React только при совсем тепличных условиях, любое реальное приложение выходит за рамки этих условий.
Пример "выхода за рамки условий" приведёте? И, кстати, что за условия?
Нет, не хочу. И тут мы прошли по кругу и вернулись к началу. А говорили про отложенный (не ленивый!) рендер.
Под условиями я имел ввиду выход за рамки маленькой демки. Вы сами ниже с горем пополам согласились, что дивы будут пересоздаваться, если явно не указать id-шники им. И никакие обсёрваблы этому не помогут (хотя да, помогут, если каждый див вынести в отдельный mol_view). Это не говоря про то, что должно быть маленькое приложение, а чем оно больше разрастается — тем больше становится запутанными взаимосвязи между вьюшками-дивами. Отлаживать это всё добро, да ещё и со странными стектрейсами — то ещё удовольствие. Объединение компонентов ожидаемо приводит к падению производительности. Ибо вся магия в ручном разделении.
Вы сами ниже с горем пополам согласились, что дивы будут пересоздаваться, если явно не указать id-шники им.
Вообще-то я сразу подчеркнул, что дом-узлы в случае $mol_dom_jsx реиспользуются по идентификаторам. И это лишь пример jsx рендерера без vdom. Можете написать свой с автогенерацией идентификаторов в духе реактовых "1.3.2.1.2". Но я за человекопонятные идентификаторы.
$mol_view же хоть и добавляет идентификаторы, но не зависит от них. Там каждый компонент сам создаёт себе узел и кеширует его у себя.
чем оно больше разрастается — тем больше становится запутанными взаимосвязи между вьюшками-дивами.
Не становятся. Ну вот, например, приложение для чтения статей с ленивой загрузкой, отображением индикатора загрузки, визуализации маркдауна и боковым меню. Все взаимосвязи весьма наглядно представлены.
Отлаживать это всё добро, да ещё и со странными стектрейсами — то ещё удовольствие.
Что странного в наших стектрейсах?
Объединение компонентов ожидаемо приводит к падению производительности.
Любой компонент является композицией других компонент. Ни к какому падению производительности это не приводит.
Можете написать свой с автогенерацией идентификаторовВы просили указать — я указал. Если писать полноценный vDOM, то он превратится в (P)React. Что и требовалось доказать. Ваше залихвастское утверждение «да я тут за 5 минут на коленке написал ваш ненужный на самом деле вдом» не подтвердилось.
весьма наглядно представленыОчень интересно, когда вы агрессивно рекламируете свою библиотеку, но при этом напрочь игнорируете чужое мнение и напропалую используете демагогические приёмы. Нет, tree не нагляден. Нет, я говорил не про демки. Давайте начнём хотя бы с 10k sloc.
Если писать полноценный vDOM, то он превратится в (P)React.
Не нужно писать vDOM.
вы агрессивно рекламируете свою библиотеку
Процитируйте, пожалуйста, "агрессивную рекламу".
напрочь игнорируете чужое мнение и напропалую используете демагогические приёмы
А вам не приходило в голову, что ваше мнение может оказаться неверным, и люди не соглашаются с вами не потому, что они такие упёртые бараны?
Нет, tree не нагляден.
Обоснуйте.
Давайте начнём хотя бы с 10k sloc.
http://mol.js.org — 25ksloc
люди не соглашаются с вамиМоё мнение?) Мне совершенно не интересно, соглашаетесь вы или нет со мной. Вообще-то это я с вами не соглашаюсь и вместе с другими порасспрашивал про то, что вы понаписали. Расспросил, спасибо. Теперь мне более понятно, что вы понаписали, поэтому вы перешли к упёртым баранам и точкам зрения :)
Что обосновать? Я так думаю, мне он просто не нравится (как и coffescript, например). Я знаю ваш ответ, он предсказуем: вот он, за два года не утратил актуальность, вы по-прежнему так отвечаете всем, кто с вами не согласен :) И да, это с вами люди в большинстве своём не соглашаются, судя по ссылке выше, по этому посту и так далее. И вы зря считаете, что это потому, что вы никем не понятый одиночка, ваяющий бриллиант во тьме невежества. Vue.js успел стать популярным, хотя был написан одиночкой (и он до сих пор поддерживается практически только им), а заматерев, перешёл на ненавистный вами vDOM.
mol.js.org — 25kslocВот точка входа, судя по всему? А рядышком в meta.tree инклуды, где 6 десятков других tree-файлов (которые кушаются вместе с .ts). Даже с учётом простой плоской структуры сего одностраничника выглядит крайне неудобно и малопонятно где какие компоненты используются. Не говоря уж про то что совершенно не ясно, как это всё работает с ts. Эта демонстрация получилась ещё более неудачной.
P.S. Вы очень зря постоянно пытаетесь вести себя в таком стиле, это отталкивает :) И совершенно зря, указывая на различия во мнениях, считаете их закостенением и/или конформизмом. Этим вы ещё более отталкиваете и собеседника и читателей. Мы все не первый год живём, некоторые даже успели повзрослеть, много раз меняли мнения на разные вещи. Не говоря уж про программирование: мы все наблюдали, как меняются языки, подходы и инструменты. И менялись вместе с ними.
Вот точка входа, судя по всему?
Нет, точка входа тут: https://github.com/eigenmethod/mol/blob/master/app/demo/index.html#L31
А рядышком в meta.tree инклуды, где 6 десятков других tree-файлов (которые кушаются вместе с .ts).
Само приложение показывает демонстрации всех компонент, что есть в бандле. В meta.tree указывается какие ещё компоненты включить в бандл, так как от них нет прямых зависимостей нигде в коде. В типичных приложениях meta.tree не используется для подключения компонент, ибо есть прямые зависимости в коде.
Не говоря уж про то что совершенно не ясно, как это всё работает с ts.
В документации всё написано. view.tree транслируется в view.tree.ts, который содержит сгенерированные классы. Через view.ts можно эти классы расширить своей логикой.
Нет, tree не нагляден.
… потому что...
мне он просто не нравится
Чудесно.
Ну смотрите, когда водишь мышью над кругами — на всей странице перестают обновляться числа. Считаете ли вы это нормальным?
Да, $mol не размазывает вычисления по фреймам, поэтому наблюдается небольшая просадка fps при обновлении чисел. На мой взгляд это лучше, чем полное отсутствие обновления.
Ну и собственно, можете заглянуть в профайлер. (Кстати, чтобы найти этот комментарий мне пришлось воспользоваться гуглом, ибо ни ctrl+f ни паджинация мне никак не помогли со страницей всех моих коментариев.)
Во-первых, надо уточнить случаи, когда
рендеринг каждого кадра важнее скачков анимации и доказать, что это так.
Во-вторых, «полное отсутствие обновления» — экстраполяция. Лучше уточнять параметры. Т.е. на слабых мобилках, при неправильно расставленных приоритетах может и да, ваш способ лучше, но это пока не делает файбер костылем.
Да с чёго бы мне расстраиваться? Адекватно — понятие относительное. Ну вот я замедлил свой комп в 5 раз:
- React15 — 1 фпс
- React16 — 60 фпс, числа не обновляются вообще
- $mol — 60 фпс, фризы на 150мс при обновлении чисел
Кстати, я тут заметил, что $mol выдавал низкий фпс (13) в этом тесте между обновлениями чисел. Причин опять оказалось 2:
$mol_view актуализирует всё состояние дом-узла, когда что-то меняется в данных. Тут точки рендерились прямо в корень, что приводило к тому, что при изменении стиля корневого элемента $mol_view проверял так же и в правильном ли порядке расположены точки. Решение — поместить точки во вложенный элемент. В Реакте, если что, сделано так же.
- Использование
touch-action
отличный отauto
приводит хром к безумно долгому обновлению дерева слоёв. Так что не стоит его использовать без крайней необходимости.
10 вкладок с треугольником на mol летают (видимо я уже обновленную версию открыл), грузят где-то 10%, а на реакте под 100% все ядра грузят и вычисления стоят на одной цифре, правда анимация наиплавнейшая.
У вас, я так понимаю срабатывает трюк: если вкладка не видна, вычисления засыпают.
У меня Реакт тоже пессимизируется в фоновых вкладках, правда не сразу, а через несколько секунд. Думаю тот дело в том, что $mol_atom все вычисления откладывает в requestAnimationFrame, который перестаёт работать сразу при скрытии вкладки. В Реакте же вычисления инициируются сразу в обработчике setTimeout а в requestAnimationFrame откладываются лишь, через 10мс работы. А setTimeout в фоновых вкладках пессимизируется не так радикально, как requestAnimationFrame.
Ну смотрите, когда водишь мышью над кругами — на всей странице перестают обновляться числа. Считаете ли вы это нормальным?
Конечно считаю ненормальным. Но вы обманываете сами себя, в реакте нет такого. Эта демка была сделана давно и на нестабильной ранней имплементации.
Сейчас не надо оборачивать setState в ReactDOMFiber.unstable_deferredUpdates, как и самого ReactDOMFiber не стало.
Ещё один обман: это не бенчмарк, а сравнение плавности анимации в реакт15 и в реакт16. Ну и да, присоединюсь к людям выше, у меня тоже мол намного медленней работает, чем вы рассказываете.
Я попробовал подключить версию 16.0.0 — получил 2.5 фпс. Ну да, в 3 раза быстрее React15 :-)
Если вы знаете как сделать правильно, чтоб не тормозило — сделайте пул-реквес (https://github.com/nin-jin/sierpinski), а не рассказывайте, что я сам себя обманываю.
Есть механизм установки дедлайнов, приоритетов, насколько я понял, он теперь различает анимацию и приоритизирует ее ниже, чем пользовательский ввод, может не рендерить невидимые узлы и т.д.
А пример с серпинским устаревшим выглядит, других пока нет, к сожалению.
Раз уж вы хотите померяться письками с реактом, то вот вам демка: build-mbfootjxoo.now.sh. В js указан sourcemap, так что исходники там есть (понятия не имею, есть ли оригинальный репозиторий в открытом доступе).
Треугольник серпинского для актуальной версии мне тоже интересно будет сделать. Но не вижу никакого смысла добавлять его в ваши «бенчмарки» :)
Этот треугольник серпинского — заведомо сложный пример для реакта
Который был написан самими фейсбуковцами для рекламы файбера. Это не "мои бенчмарки".
как и для любой библиотеки, которая не отсекает сама дивы за краем экрана
Это демо вообще не про отсечение. Если у вас треугольник не влезает в экран — просто уменьшите зум.
А в каждый кружок специально добавлен долгий цикл на 80 миллисекунд.
В том-то и дело, что не в каждый кружок, а в каждую группу кружков/подгрупп и не 80мс, а по 0.8мс на группу. Именно поэтому реакт и тормозит, что ему чтобы обновить значения кружков нужно пересчитать все группы, а это долго. Использование реактивного программирования позволяет не пересчитывать группы на каждый чих. Поэтому $mol обновляет содержимое кружков за 35мс, а Реакт — за 385мс. А всё, что Делает РеактФайбер — размазывает эти 385мс на 40 кадров.
Который был написан самими фейсбуковцами для рекламы файбера. Это не «мои бенчмарки».
Анимацию он хорошо показывает по-сравнению со старым реактом и производит вау-эффект, для этого наверное и создавался. Примеров мало, что б делать выводы какие-то.
Который был написан самими фейсбуковцами для рекламы файбера. Это не «мои бенчмарки».Не знаю, что вы себе напридумывали, продолжая обманывать себя, но он не написан для рекламы файбера. Он написан для того, чтобы показать разницу между двумя версиями реакта. Если бы они рекламировали скорость работы рендера, то сделали бы летающих по странице 10 тысяч пикачу.
Точно, 0.8 мс) Но нет, реакт не тормозит тут. Это ненормальные условия, где реконсиляция занимает намного больше обновления ДОМа. Это замедление было сделано специально, чтобы показать разницу между двумя версиями реакта. Мол получает тут сравнимую скорость только из-за того, что эти библиотеки разные, и ненормальные условия по-разному влияют на них. В реальных приложениях такого замедления нет (ваш к.о.), мол не получит такого выигрыша.
Дмитрий, вы не согласны, что это не бенчмарк?))
Это ненормальные условия, где реконсиляция занимает намного больше обновления ДОМа.
Не реконциляция, а рендеринг приложения в vDOM. Какой-нибудь сложный фильтр в реальном приложении запросто может занять и куда больше времени.
Терминологические споры мне вести не интересно.
А я не предлагаю)) Просто мне кажется забавным, как вы ухватились за эту демку.
И какой же сложный фильтр может быть в функции рендера?))
Товары с популярностью выше среднего в тексте названия, типа и описания которых встречаются подстроки "сини" и "штан".
1 — размазывание рендеринга по фреймам никак не поможет ctrl+f полноценно заработать раньше окончания всего рендеринга. В худшем случае он может вообще никогда не завершиться, если постоянно будут более приоритетные задачи (скриптовая анимация какая-нибудь в верху страницы).
2 — Реакт размазывает по фреймам лишь свой маленький кусочек работы. Есть ещё большой пласт работ по подготовке данных, по отрисовке оных и прочие задачи, на которые банально может не хватить времени до конца фрейма, большая часть времени которого уже потрачена на рендеринг невидимых частей.
3 — очевидно, ваше требования было в том, что бы работал поиск. Необходимость оного никто не оспаривает. Я объяснил, чем нативный поиск плох и почему ему лучше предпочесть встроенный. На нормальном фреймворке с двусторонним связыванием такие поиски делаются в 3 строчки кода.
4 — reflow (обновление дерева css-блоков), repaint (отрисовка css-блоков), recomposition (формирование итоговой картинки) — это части браузерного пайплайна, которые отрабатывают уже после внесения изменений в DOM.
5 — ничто не мешает по ctrl+f фокусировать встроенный поиск.
Вы попробуйте своё прожорливое приложение на среднестатистической мобилке.
1. См. пост выше про обман себя. У реакт@16 нет такого, что он может не вывести числа. Полный рендер большой портянки гридов завершится не за один присест с фризом, а за несколько кадров без фризов. Для пользователя это будет относительно незаметно, так как большая часть элементов будут уже не видны, а первые (видимые во вьюпорте) появляются сразу же.
2. Но мы же говорили именно про оптимизацию рендера :) Проблемы с тяжелой работой с данными, не относящиеся к реакту, будут в любом случае, это отдельный вопрос, никак не относящийся к нашему обсуждению. В остальном — кооперативная мультизадачность тасков рендера в реакте поддерживает приоритет операций, и для операций от пользователя он наивысший.
3. Нет, моё требование было в том, чтобы пользователь мог тут же проскроллить к нужному месту большую портянку. Она должна быть сформирована как можно раньше, отложенно, но не лениво. Ctrl+F у нас вообще был невозможен, это же canvas, как упомянул выше. В случае мол это является большой проблемой, которую вы игнорируете с завидным упорством. А в ваш бенчмарк "Время вывода значительных объёмов данных в WEB приложениях" надо добавить ещё один шаг: пара десятков прокруток на произвольные места отрендеренного списка. Но этого никогда не будет сделано, так как вы любите обманывать себя.
4. Ваши исходные слова:
Чем больше элементов в ДОМ, тем медленнее происходит reflow, repaint и прочее.Вы что, думаете реакт по отдельности каждый див вставляет?) А минимизировать изменения ДОМа как раз реакт помогает.
5. Это сводится к тому же самому:
— у вас в демке с вашим мол скролл фризит намертво всю страницу!
— а вы скролл не дёргайте.
В итоге вы предлагаете костыль вместо привычного решения. И не факт, что полнотекстовый поиск вообще реализован. И не всегда его можно будет реализовать) Например на страничке с 22-я агрегированными списками (внутренний инструмент для отчётов по разным срезам). Мы там наоборот специально пришли к браузерному Ctrl+F как к наиболее удобному решению. И да, мы костыль с перехватом Ctrl+F никак не решает основной проблемы: возможности сразу начать скроллить. Молу от этого плохеет настолько, что страница фризится намертво.
То наше приложение из пункта 3 работало на IPad 2. Который старый, а не новый mini.
Проблемы с тяжелой работой с данными, не относящиеся к реакту, будут в любом случае
Если что-то можно не рендерить, то можно и не подготавливать для него данные и даже можно их не загружать. Ленивый рендеринг имеет системный эффект. Пока вы только ещё готовите данные для передачи их реакту для рендеринга портянки блоков, я уже покажу те, что видимые.
Нет, моё требование было в том, чтобы пользователь мог тут же проскроллить к нужному месту большую портянку.
К нужному месту скроллбаром у него не получится. А вот поиск бы помог.
Но этого никогда не будет сделано, так как вы любите обманывать себя.
Не поэтому, а потому что это довольно маргинальные кейсы. Если для вас они важны — можете заморочиться и запилить пул-реквест.
Вы что, думаете реакт по отдельности каждый див вставляет?)
Вам сюда: https://webo.in/articles/all/2009/31-rending-restyle-reflow-relayout/
В итоге вы предлагаете костыль вместо привычного решения.
Я настаиваю на удобном для пользователя решении. Стандартный поиск весьма не удобен.
Итого мы прошли кучу обсуждений, где вы доказывали, что всё работает и всё получится, и теперь заявляете, что не получится. Спасибо, что рассказали :)
И какие же это маргинальные кейзы, если каждый второй человек в комментариях вам писал об этом?) Н
Вы прямо светоч знаний. Правда по-прежнему непонятно, зачем вы это в ответ на необходимость отрендерить список. Только не говорите, что вы пугаетесь много рендерить и из-за этого даёте пользователям страдать, когда все первым делом тянутся к скроллу и мол фризит вкладку :)
И опять же, каждый второй пользователь вам говорил, что это неудобно. Но вам виднее :)
Мы реакт обсуждаем. С чего вы взяли, что мой сервис/сага будут готовить данные для невидимых компонентов?))
С того, что Реакт имеет ведомую архитектуру. Что для него подготовили — то он и отрендерил. $mol, наоборот имеет, ведущую архитектуру — что он запросил, то для него и подготовили. Вам, чтобы инвертировать архитерктуру, нужно будет вручную поплясать вокруг детектирования видимых элементов данных в реальном времени и подготовкой только нужного.
И какие же это маргинальные кейзы, если каждый второй человек в комментариях вам писал об этом?)
Поиск малейшего изъяна и выдача его за огромную дыру — типичная реакция, когда покушаются на святое.
Вам, чтобы инвертировать архитерктуру, нужно будет вручную поплясать вокруг детектирования видимых элементов данных в реальном времени и подготовкой только нужного.Простите, но я же сам говорю реакту, что рендерить :) Что же такого уникального в ваших десяти строчках? Только вы правильно умеете плясать вокруг детектирования и расширять точку до кругозора? :) Вы в курсе, что реализаций виртуального скроллинга великое множество?))
Поиск малейшего изъяна и выдача его за огромную дыру — типичная реакция, когда покушаются на святое.Понятно, вопросов больше нет :D
Судя по всему эта ветка тоже закончена, здесь мы обсудили все пункты. Не утруждайтесь, вопросы выше в этом моём комментарии — риторические.
Простите, но я же сам говорю реакту, что рендерить :)
О том и спич, что вам нужно всё-время самому ему говорить что рендерить. Мне же достаточно сказать "сам разберись какие данные тебе нужны и запроси их у меня". Вы бы не смайлики мне ставили, а попробовали разобраться в описываемой мной парадигме — она значительно упрощает разработку.
Вы в курсе, что реализаций виртуального скроллинга великое множество?))
$mol_list — ни разу не виртуальный скроллинг. Но виртуальный скроллинг у нас тоже есть.
А каким образом так вышло, что mol-jsx работает вдвое быстрее, чем mol, и требует меньше памяти?
Таким, что реализация бенчмарка на $mol_dom_jsx была сломана и ничего не рендерила. Починил.
Я думал, что mol-jsx — что-то вроде обертки над чистым mol, логично, что он должен работать медленнее?
Нет, к $mol компонентам $mol_dom_jsx отношения не имеет. Это просто рендеринг всего дома через JSX без Реакта, не более.
И оно сразу стало на уровне нейтив-дома. И ангуляр, и реакт — тоже на том же уровне, и только мол каким-то образом в n раз быстрее. При этом с увеличением количества нод затраты на обновление не растут. Выглядит откровенно странно.
При ленивом рендеринге затраты пропорциональны не объёму данных, а размеру экрана.
Ну давайте рассмотрим такой случай, когда все данные влезают на экран. Допустим, какой-то сложный грид, и там на экране десять тыщ таких вот блоков, они все видны. Как работает в этой ситуации ангуляр или там реакт — я вижу, а ваш фреймворк как себя поведет? Сдюжит? Нет?
Ну и ссылку на исходники всех этих тестов было бы неплохо иметь.
Допустим, какой-то сложный грид, и там на экране десять тыщ таких вот блоков, они все видны.
Видимых блоков будет не больше 1000. Ну вот для примера такой грид — всего 700 дом-элементов на моём фулхд экране.
Ну и ссылку на исходники всех этих тестов было бы неплохо иметь.
С чего бы это?
> Ну вот для примера такой грид — всего 700 дом-элементов на моём фулхд экране.
Так там по блоку на ячейку, а их легко может быть и десяток, и полсотни. Умножаете 700 на 10 — получаете 7к. Как ведет себя $mol с 7к видимыми элементами?
Ячейка с 10 блоками внутри и занимать будет в 10 раз больше места, а значит таких ячеек влезет в 10 раз меньше. Число видимых блоков пропорционально площади экрана.
Нафантазировать можно много чего. Давайте ближе к реальности — в каком приложении требуется 10к блоков в видимой области?
Вопрос в другом, почему mol на видимой области медленнее vdom, чему там тормозить, если обновления точечные и все-равно все упирается в DOM?
- Не всё упирается в ДОМ. Остальные вычисления тоже имеют свою цену. И обеспечение ленивости в том числе.
- В зависимости от бенчмарка может выдать меньше попугайчиков, а может больше.
- Не тормозит. Цель $mol — 200мс в худшем случае, а не 10мс в лучшем.
Ну, давайте более конкретно..
ToDoMVC, 30 задач, отзумили страницу, чтобы все влезали (загрузка, создание, удаление):
JavaScript Vanilla JavaScript 390 ms 258 ms 63 ms
$mol 393 ms 297 ms 116 ms
Knockout.js 445 ms 367 ms 137 ms
Polymer 965 ms 375 ms 381 ms
AngularJS Angular2 1692 ms 409 ms 224 ms
AngularJS 1082 ms 678 ms 264 ms
React React + Alt 722 ms 748 ms 386 ms
Vue.js 498 ms 918 ms 843 ms
Отстали бы те, кто использует аргумент ленивости против mol.
Т.к. я использовал атомы (основу mol) вместе с реактом и делал бенчмарки, то думаю, что mol все-равно бы в среднем чуть обгонял популярные фреймворки за счет легковесности.
Даже если скорость будет такая же, она достигается гораздо более простыми алгоритмами, в 5 раз меньшим объемом кода, кастомизируемостью, которых нет у других, а это уже аргумент.
Я думаю не стоит усложнять интерфейс и реализацию ради столь маргинальных кейсов. Отключить ленивость-то не сложно (установкой minimal_height=0 для всех блоков или отказом от $mol_list), но код получится не идеоматичным. Всё же бенчмарки — это не просто конкурс "кто из эквивалентных реализаций наберёт больше попугайчиков", а способ оценить отзывчивость приложения в различных архитектурах и различных ситуациях по умолчанию и объём трудозатрат, чтобы эта отзывчивость не деградировала. В реальном приложении же никто в здравом уме не будет отключать ленивость — почему это надо делать для бенчмарков, ещё более отдаляя их от реальности?
Ещё показательный пример — бенчмарк вывода графиков. $mol мало того, что выводит всё через один элемент path, а не кучу circle, так ещё и точки выводит не все, схлопывая в одну те, что располагаются слишком близко. Не честно? На Highcharts тоже можно сделать просеивание? Можно, только делать это придётся вручную и скорее всего уже после того, как пользователи достанут саппорт своими жалобами по поводу тормозов, когда они загружают в систему слишком много данных.
Так что я против того, чтобы уравнивать все реализации по самой глупой из них ради никому не нужной "справедливости". Главное — какой видимый результат для пользователя, а не сколько пустой работы было проделано.
А если стена из мониторов, с очень большим суммарным разрешением?
А если это приложение на каком-нибудь electron или phantomjs, задача которого срендерить много-много страниц pdf? Хотя тут наверное SSR поможет, а он у вас без такой оптимизации.
Как будет масштабироваться mol в сравнении с конкурентами на нестандартные решения? Хоть это сейчас и редкие случаи, но интересный был бы задел на будущее.
Ну так, если стена из мониторов, то и результаты на тех же бенчмарках будут другие. Взял бенчмарк — протестил в своих условиях — что может быть проще? :-) Пытаться экстраполировать данные полученные на маленьком экране на большой — гиблая затея. Например, даже в том же ToDoMVC при увеличении размеров экрана начинает всё сильнее сказываться repaint, который от фреймворка мало зависит.
Во-первых, мол некорректно обрабатывает этот бенчмарк (не обновляются todo-счетчики), во-вторых — непонятно, чего вы там назапускали, т.к. вот:
i.imgur.com/vea9fYe.png
мол некорректно обрабатывает этот бенчмарк (не обновляются todo-счетчики)
Какие ещё счётчики? Если речь про число открытых задач в подвале, то оно обновляется.
непонятно, чего вы там назапускали
Я в ФФ запускал под Убунтой, Хрома у меня сейчас нет под рукой.
В остальных версиях бенчмарка стоит обновляющийся счетчик на _каждой_ задаче.
Что за счётчики-то? Можно скриншот?
i.imgur.com/8Q8Ie8t.png
реакт:
i.imgur.com/fp83cgX.png
ангуляр и другие фреймворки аналогично реакту — на каждом todo по счетчику
кстати, выставлены ли в реакте/ангуляре ключи?
ну и еще, там с одной и той же стороны блоки удаляются?
Так вот оно что. У вас просто экран маленький и название задач обрезается. Отзумьтесь и увидите названия целиком.
Ключи выставлены, можете сами посмотреть.
Блоки удаляются посредством клика по кнопке удаления самой первой задачи.
Да, действительно, извиняюсь.
> Ключи выставлены, можете сами посмотреть.
<li *ngFor="#todo of todoStore.todos" [class.completed]="todo.completed" [class.editing]="todo.editing">
и где trackBy?
Нет, не будет. Просто в реальности она будет не пустая, а с контентом, а контент на 10 блоков — ну это даже не много.
Вы можете использовать любой шаблонизатор, который умеет патчить дом. Да хоть даже JSX с соответствующей реализацией:
class MyHello extends MyComponent {
@observable name = 'Anonymous'
@computed render() { return [
<div id={ this } className="my-hello" >
Hello,
<strong id={ this + '.name' } >
{ this.name }
</strong>
!
</div>
) ]
}
Каждый раз при изменении данных будет перезупаскаться render
, который будет патчить реальный дом по идентификаторам и возвращать список дом-нод.
Формируете новый список dom-узлов, а специальная функция изменяет дом-узел,
чтобы список дом узлов оказался таким, каким надо. Простейшая реализация такого патча дома занимает всего 50 строк.
class MyHelloList extends MyComponent {
@observable helloList = [
new MyHello({ id : this + '.hello=first' )} ,
new MyHello({ id : this + '.hello=second' })
]
@computed render() { return [
<div id={ this } className="my-hello-list" >
{ ... this.helloList.map( item => item.render() ) }
</div>
) ]
}
обычная ф-я, то на каждое изменение у вас будет перерендериваться весь дом, потому что вычислить ф-ю «частично» — нельзя
Не будет. Функция будет перезапускаться, но то, что она вызывает, не обязательно будет создавать новые дом-узлы. Реализация по ссылке будет реиспользовать существующие узлы, например.
апдейт внутреннего computed инициирует пересчет всех computed вверх по дереву зависимости.
Нет, не инициирует. В том и основное отличие от собственно Реакта, которому необходим пересчёт всех функций выше для того, чтобы увидеть изменения в глубине.
либо заизолируете все за нцатью слоями абстракции (как это, кажется, сделано в вашем фреймворке).
Да нет, там тривиальная реализация — обычный дифф с реальным состояним узла:
render() {
$mol_dom_render_children( this.dom_node , this.child_nodes )
$mol_dom_render_attributes( this.dom_node , this.attributes_dictionary )
$mol_dom_render_styles( this.dom_node , this.style_dictionary )
$mol_dom_render_fields( this.dom_node , this.field_dictionary )
}
Не будет. Функция будет перезапускаться, но то, что она вызывает, не обязательно будет создавать новые дом-узлы. Реализация по ссылке будет реиспользовать существующие узлы, например.
Ну и чем это все в таком случае отличается от vdom реакта?
Сестринские узлы будут, конечно, закешированы, но все узлы вверх будут перестроены.
> Нет, не инициирует. В том и основное отличие от собственно Реакта, которому необходим пересчёт всех функций выше для того, чтобы увидеть изменения в глубине.
@computed будут пересчитаны
Сестринские узлы будут, конечно, закешированы, но все узлы вверх будут перестроены.
Не будут.
@computed будут пересчитаны
@computed пересчитывается только при изменении зависимостей. Если зависимости не меняются — @computed не пересчитывается.
Любая внутренняя нода — зависимость (внутренний @computed). Если какая-то внутренняя нода поменялась, то весь дом вверх следует пересчитать.
Не весь, а лишь вычислить содержимое ноды уровнем выше. И только если нода поменялась, что зачастую не так (привет реиспользование существующих нод).
Которая, в свою очередь, является @computed значением для своего предка, тот — @computed для своего, и так до корня.
> И только если нода поменялась, что зачастую не так (привет реиспользование существующих нод).
В рассматриваемом примере @computed render() ВСЕГДА возвращает другую ноду, если какая-то зависимость изменилась. Чтобы этого избежать — вам придется заменить простую ф-ю render на некую хитрую сущность, которая будет сама менеджить ноды определенным образом при помощи некоей внутренней магии. Короче — получится то, что выдает компилятор ангуляра из темплейтов :)
Давайте вы не будете фантазировать, а посмотрите в реализацию?
- $mol_dom_jsx — адаптер для JSX
- $mol_dom_make — создание и реиспользование узлов
- $mol_dom_render — обновление состояния узлов
@computed render() { return [
<div id={ this } className="my-hello-list" >
{ ... this.helloList.map( item => item.render() ) }
</div>
) ]
в реакте взыов (div) создает новую ноду в вдоме. Вы говорите, что у вас оно не создает новую ноду, а берет уже существующую (как он кстати, ее находит?), потом проходит алгоритмом чендж детекшена и меняет только те ноды, которые изменились, так? Каким образом он определяет какой реальной ноде дома какой терм кода соответствует, если это невозможно сделать для тьюринг-полного языка (каковым является jsx) без исполнения самого кода?
в реакте взыов (div) создает новую ноду в вдоме.
И при чём тут Реакт?
Вы говорите, что у вас оно не создает новую ноду, а берет уже существующую (как он кстати, ее находит?)
По идентификатору.
Каким образом он определяет какой реальной ноде дома какой терм кода соответствует
Никак не определяет. Упомянутый код:
<div id={ this } className="my-hello" >
Hello,
<strong id={ this + '.name' } >
{ this.name }
</strong>
!
</div>
Транслируется компилятором в:
$mol_dom_jsx( 'div' , { id : this , className : "my-hello" } ,
"Hello" ,
$mol_dom_jsx( 'strong' , { id : this + '.name' } , this.name ) ,
'!' ,
)
Отлично, теперь давайте проверим, насколько я верно все понял.
1. Если идшники не указать, то все ноды будут пересоздаваться, верно?
2. Допустим, был дом (div id=1 class=foo (div id=2 class=bar)) и мы рендерим render() = (div id=1 class=bar (div id=2 class=foo)). В данном случае никто никаких нод пересоздавать не будет, а просто у существующих нод поменяются class, так?
3. Допустим был дом тот же, что и раньше, (div id=1 class=foo (div id=2 class=bar)), мы рендерим render() = (div id=2 class=bar (div id=1 class=foo)), что вообще произойдет?
4. Что будет если я отрендерю несколько нод с одним ид? То есть, усложним предыдущий вариант — тот же оригинальный дом, но ф-я (div id=2 class=bar (div id=1 class=foo (div id=2 class=yoba)))
- Да
- Да.
- Ноды поменяются местами.
- Один идентификатор — одна нода. В вашем примере mobx упадёт с циклической зависимостью.
Что это значит? Какой дом в результате получится и какая нода будет какому терму ставиться в соответствие? И получается что семантика JSX тут полностью сломана, т.к. результирующий дом не зависит от ф-и render? Зачем вообще тогда ф-я render, jsx и остальное, если оно ничего не делает?
> Один идентификатор — одна нода.
А если их будет все-таки две, например, в разных сестринских элементах?
Какой дом написали — такой и получится. Что не понятно?
render() {
if( this.twoInOne ) {
return (
<div id="1" class="foo">
<div id="2" class="bar" />
</div>
)
} else {
return (
<div id="2" class="bar">
<div id="1" class="foo" />
</div>
)
}
}
При переключении twoInOne будет пара insertBefore и всё.
Ссылаться идентификатором вы можете на одну ноду из разных мест.
Вы выше сказали, что дом строится на основе существующего, а не на основе того, что написано в рендере. Вот я и пытаюсь понять, как это все увязывается.
> При переключении twoInOne будет пара insertBefore и всё.
Вы, пожалуйста, ответьте на вопрос из позапредыдущего поста. Какой дом получится в пункте три, и почему именно такой? И зачем нужна ф-я рендер, если структура итогового дома никак не связана с ее jsx-описание в рендер?
> Ссылаться идентификатором вы можете на одну ноду из разных мест.
То есть если я вызову render() = (div id=1 class=foo), и у меня есть десяток нод с ид=1, то они все 10 обновятся, при том не важно когда и как вызвана данная render, верно?
Вы выше сказали, что дом строится на основе существующего, а не на основе того, что написано в рендере.
Берётся существующий и изменяется до написанного.
То есть если я вызову render() = (div id=1 class=foo), и у меня есть десяток нод с ид=1, то они все 10 обновятся
Если вы рендерите через предложенный JSX адаптер, то у вас не может получиться в реальном доме несколько нод с одним идентификатором. Если вы таких нод надобавляли другими путями, то JSX возьмёт первую из них. Вы правда не в состоянии прочитать простейший JS код? Или принципиально не ходите по ссылкам? Ну давайте я вам сюда выгружу:
export function $mol_dom_make( id? : string , localName = 'span' ) {
const document = $mol_dom_context.document
let node = id && document.getElementById( id ) as Element
if( !node ) {
node = document.createElement( localName )
if( id ) node.id = id
}
return node
}
export function $mol_dom_jsx(
Elem ,
props ,
...children
) {
let node
if( typeof Elem === 'string' ) {
node = $mol_dom_make( props && props['id'] , Elem )
$mol_dom_render_children( node , [].concat.apply( [] , children ) )
$mol_dom_render_fields( node , props )
} else if( typeof Elem === 'function' ) {
node = new ( Elem as any )({ childNodes : children , ... props })
if( node.render ) node = node.render()
}
return node
}
остальные нормально диффаются и не пересоздаются сами по себе
Ага, конечно. Изменение родителя приводит к полному пересозданию поддерева.
То есть, по сути, переложили задачу диффа на плечи программиста
В каком это месте?
проблемы с повторными и кривовыставленными идшниками
Так не ставьте кривые айдишники.
Только в вдом, операции на котором быстрые. В реальном дом — не приводит.
> В каком это месте?
Как в каком? Надо проставлять идшники, фиксируя принадлежность нод.
> Так не ставьте кривые айдишники.
Хорошая история, но вообще фреймворк должен следить за такими вещами.
Только в вдом, операции на котором быстрые. В реальном дом — не приводит.
Надо проставлять идшники, фиксируя принадлежность нод.
Это не "задача диффа", а "задача идентификации", которую никакой фреймворк за программиста не сделает. Вернее он может попытаться угадать, как это делает Реакт, и неизбежно угадать неправильно, как в примере по ссылке выше.
Ну правильно, вы же положили инпут в ту часть дерева, что постоянно пропадает и появляется.
Вот другой пример: http://jsbin.com/joyafepela/edit?js,output Второе поле не пересоздается. Потому что не меняется.
Что сказать, сделайте свой mol популярней реакта и им будут пользоваться, а пока я не видел вакансии $mol девелоперов. Да и знак $ смотрится просто ужасно.
Другими словами, Вы ставите вопрос так — зачем этот неправильный реакт, когда есть мой правильный mol. Я правильно я Вас понял?
Нет, $mol_view, конечно, построен по тому же принципу, но сейчас речь не о нём. Ренедерить вы можете любой библиотекой, умеющей применять изменения к дому. $mol_dom_jsx — пример такой библиотеки, написанной на коленке за пол часа.
Иначе Вы предлагаете каждому создавать свою систему рендера, которая ну никак не может с помощью 50 строчек кода покрыть все потребности, также как и предугадать их все
Да их кот наплакал, этих потребностей:
- Задать список дочерних dom-узлов
- Задать словарь атрибутов
- Задать словарь dom-свойств
- Задать словарь обработчиков событий
Какие ещё у вас могут появиться потребности?
Что сказать, сделайте свой mol популярней реакта и им будут пользоваться
Что раньше появилось: курица или яйцо?
а пока я не видел вакансии $mol девелоперов.
У нас открыта вакансия в Питере. Приходите, научим.
Да и знак $ смотрится просто ужасно.
Зарплата будет в рублях, не волнуйтесь. :-)
Для этого надо само дерево создавать в рамках того же подхода, то есть задать его как @computed render и менеджить через MobX. Но тогда придется прибить гвоздями слой рендера к слою менеджера состояний.
Я бы не выделял "менеджмент состояний" в какой-то отдельный слой. Это по определению инфраструтурное средство для реализации коммуникации. Нет ничего плохого в том, что все слои будут коммуницировать единообразною
Я бы тоже не стал, мы этот вопрос уже, к слову, обсуждали, но так уж исторически слоилось что сам дом (либо какие-то его представления типа вдома) не включается в стейт.
Хорошим дополнением к статье будет запись доклада от Michel Weststrate (автора MobX) на ReactEurope@2017.
Или его же недавняя статья (28 sep 2017), в ней в т.ч. есть упоминание об одном из минусов MobX.
Очень жаль, что вы увидели в моих словах лишь то, чего в них не было, и совсем не заметили того, о чём шла речь. (Подсказка: при использовании mobx или любой другой реализации ОРП, Реакт или любая другая библиотека виртуального дома — как собаке пятая нога).
Если вы имели ввиду
Я вроде бы вполне доступным языком написал, что имею ввиду:
при использовании mobx или любой другой реализации ОРП, Реакт или любая другая библиотека виртуального дома — как собаке пятая нога
Где вы тут увидели $mol?
К сожалению (или к моему счастью?), пока что точка зрения mayorovp и Druu мне больше импонирует.
Каждый человек имеет некоторый горизонт взглядов. Когда он сужается и становится бесконечно малым, то превращается в точку. Тогда человек говорит: «Это моя точка зрения». (с) Давид Гильберт
Ай не хорошо как на личности переходить :) Попробуйте найти другие пути, как рассказать про вашу крутую библиотеку.
Там ссылка на вашу же статью с рекламой вашей библиотеки.
Статья про парадигму программирования. Примеры кода приведены с использованием моей библиотеки, так как только она поддерживает все упоминаемые фичи парадигмы. Ну а ссылку я дал на конкретную часть выступления, посвящённую бессмысленности и беспощадности архитектуры React-а.
Почему-то всё замыкается всегда на вашу библиотеку
Не я увёл обсуждение в сторону $mol. Впрочем, $mol во всю использует упомянутые в статье принципы, хоть и без mobx.
Ай не хорошо как на личности переходить :)
Это лишь предложение расширить кругозор, а не цепляться за привычную точку зрения.
Попробуйте найти другие пути, как рассказать про вашу крутую библиотеку.
А давайте побрейнштормим. Вот есть инструмент. Позволяет писать мало, а делать много. Масштабируемая архитектура. Стоимость использования нулевая. Поддержка бесплатная и оперативная. Как о нём рассказать, чтобы у него появилось хотя бы 100 звёзд на гитхабе?
2. Нужны примеры в сравнении с аналогичными на react
3. Статьи на англоязычные ресурсы
4. Посмотреть на опыт авторов mobx, catberry, авторы которых с нуля и быстрее вас получили намного больше звезд
А в реальности, мне кажется, что только много денег, вложенные в маркетинг, могут помочь, у меня то всего 20 звезд на гитхабе, около 100 у вас это еще неплохо =)
Например, может лучше начать с mol_jsx, вместо tree
Тогда потеряется львиная доля преимуществ и получится шило на мыло.
Посмотреть на опыт авторов mobx, catberry
А где на него посмотреть?
Тогда потеряется львиная доля преимуществ и получится шило на мыло.Что-то потеряется, к сожалению. Мое предположение в том, что из-за конкуренции в вебе и js, вам без огромных вливаний ресурсов, как у гугла в ангулар, не продать огромный кусок чего-то сильно отличающегося.
У всех космические корабли похожи на энтерпрайс, у вас на инопланетную губку из водорослей, где все замкнуто на вас:
1. Свои соглашения по импортам и связянного с ними стилем кодирования
2. Необычная реализация инверсии контроля через контексты
3. Своя экосистема: пакетный менеджер и сборщик зависимостей (mam)
4. Своя библиотека для DOM, http и пр. утиль
5. Своя библиотека работы со стейтом (mol_atom), с удачной, но непривычной идеей в основе
6. Иерархическое наследование на tree, вместо плохо кастомизируемой, но простой и понятной всем композиции на шаблонах, таких как JSX
Это все может быть 100 раз оправданно, цельно и логично и опережать время лет на 5, но оно просто ни на что не похоже. Аналогии не работают, т.к. это новый подход к разработке в целом. Может попробовать продать маленький кусок, встроив в привычную экосистему, как автор mobx?
А где на него посмотреть?Мне б тоже хотелось знать, может просто их спросить? Я помню как автор mobx много где упоминал свое решение пару лет назад, тогда еще mobservable, например как здесь и у него было пару десятков звезд, а сейчас 11К.
Когда я попробовал использовать Mobx, мне показалось, что на нем писать надо очень осторожно, иначе если упустишь что-нибудь, то придется долго искать ошибку.
Этому способствовали следующие особенности:
- Чтобы не было лишних перерисовок, все изменения лучше писать в функциях, используя декоратор action, или использовать runInAction.
- Объект желательно делать полностью наблюдаемым. В противном случае вложенные объекты не будут вызывать оповещение об изменениях.
Из-за этого при присваивании нового объекта нужно не забывать писать что-то вроде: parentObj.level1Obj = observable({level2Obj: {propA: 10}}); - Извиняюсь, но тут я уже не помню. Суть примерно следующая:
в render надо указывать полный путь (либо использовать @computed) для перерисовки при изменении какого-нибудь используемого свойства.
Например, в render писать так:
render() { return <div>{'Username: ' + Stores.users.selected.name}</div>; }
Если же в какой-нибудь переменной (вроде вне render) сохранить Stores.users, а потом написать
то обновления не будет.return <div>{'Username: ' + tempUsers.selected.name}</div>;
В общем, есть такие тонкости, которые усложняют разработку — то надо не забыть прописать, другое не забыть, третье не забыть. Может я где-то неправ, все-таки пользовался Mobx давно и не долго.
Из-за этого при присваивании нового объекта нужно не забывать писать что-то вроде: parentObj.level1Obj = observable({level2Obj: {propA: 10}});
Если parentObj.level1Obj
— наблюдаемое свойство, то так как вы написали делать не нужно. Объекты без прототипов рекурсивно превращаются в наблюдаемые автомагически.
Если же в какой-нибудь переменной (вроде вне render) сохранить Stores.users
...
… то изменение свойства users не будут наблюдаться, что очевидно. Изменения вложенных свойств наблюдаться не перестанут.
Еще показалось интересным, что в начале доклада Вы показываете кусочек кода с ручной подпиской на изменения через state.on('change'), а в конце доклада мы видим весьма похожую функцию onSnapshot, которая уже такой плохой не считается. Поясните и ее предназначение, пожалуйста.
Также как-то немного выпало из доклада, вот этот хелпер types — это часть modx?
Также как-то немного выпало из доклада, вот этот хелпер types — это часть modx?
Хелпер types — это инфраструктура mobx-state-tree. Просто mobx использует ES6 классы а не хелперы.
Еще показалось интересным, что в начале доклада Вы показываете кусочек кода с ручной подпиской на изменения через state.on('change'), а в конце доклада мы видим весьма похожую функцию onSnapshot, которая уже такой плохой не считается. Поясните и ее предназначение, пожалуйста.
Так плоха же не сама по себе подписка на событие, а тот факт, что их много, а еще дублируется render.
onSnapshot же дергается при любом изменении модели, сюда "слетаются" любые события. Кроме того, на onSnapshot никто не дергает render, это событие не для того сделано.
PS обратите внимание, в слове "MobX" третья буква — b
а не d
. ModX — это совсем другой проект.
PS обратите внимание, в слове «MobX» третья буква — b а не d. ModX — это совсем другой проект.
Увидел опечатку уже после отправки, когда нельзя было редактировать пост. А вроде бы просто налил себе чай, отвлекся буквально на минуту, эх, Хабрахабр. Имел дело и с этим другим проектом, видимо, опечатался.
Я имею в виду Fiber Architecture, асинхронный рендеринг и вот это вот все.
Ну, MobX вообще не завязан на React технически — его можно хоть с Angular использовать или еще чем-нибудь. Если речь про официальный байндинг mobx-react — то он официально поддерживает React 16. Да ну и там нечему особо сломаться-то.
Как библиотека MobX помогает управлять состоянием веб-приложений. Лекция в Яндексе