company_banner

Как библиотека MobX помогает управлять состоянием веб-приложений. Лекция в Яндексе

    Недостаток зависимостей в веб-приложении приводит к ошибкам в интерфейсе, избыток — снижает производительность. Руководитель отдела разработки интерфейсов Яндекса Азат razetdinov показывает, как библиотека MobX помогает отслеживать минимальный набор изменений и поддерживать консистентность состояния приложений, а также знакомит с инструментом mobx-state-tree, который позволяет совместить всё лучшее из MobX и Redux.



    То, что мы руками пытаемся работать с immutable-данными, — это необязательно. Immutable-состояние нашего приложения — это еще один вид, еще одно представление, еще одно отображение. Можно использовать живую модель, просто каждый раз в любой момент времени получить его плоскую проекцию.


    — Меня зовут Азат Разетдинов, я представляю персональные сервисы Яндекса: Почту, Диск, Календарь, Паспорт, управление аккаунтом. Хотел бы рассказать про управление состоянием веб-приложения без боли.

    Что такое состояние приложения? Это центральное понятие в архитектуре всего веб-приложения. Там хранится все, от чего зависят остальные компоненты. Например, самое очевидное — отображение, представление состояния приложения в виде дом-дерева, которое вы видите в браузере.

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

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

    Бывают случаи, когда мы хотим какие-то локальные изменения хранить в хранилище, чтобы оттуда потом их доставать. Хранить прямо в браузере. Здесь тоже часто требуется синхронизация состояния приложения с локальным хранилищем. На самом деле частей, которые зависят от состояния приложения, довольно много.
    В чем здесь проблема?

    Как правило, состояние приложения — дерево, где очень много данных, есть какие-то списки, объекты, хэши, примитивные данные. Большая иерархичная структура.

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

    Если вы давно во фронтенде, то, наверное, знакомы с таким паттерном, как ручная подписка на изменения.

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

    Если вспомнить старые фреймворки, это выглядело примерно так в псевдокоде.

    Сначала используем текущее состояние, подписываемся на изменения и при каждом изменении выполняем какие-то действия. Либо точечно меняем дом-элементы, как это было модно раньше, либо запускаем заново весь рендер, как это модно сейчас.

    У этого подхода две проблемы.

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

    Но у этого подхода тоже есть проблема.

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

    Что здесь произошло? Товарищ обновил свою аватарку, а она обновилась не везде. Оказалось, большая аватарка поменялась, а маленькие аватарки в твитах не подписались на изменения аватарки пользователя и не получили это изменение, не обновили себя. Это самый большой минус, который есть в ручной подписке.

    В этом месте к нам на помощь приходит MobX. Он реализует подписку ровно на те поля состояния приложения, которые вы используете.

    Чтобы это показать, нужно объяснить, как это устроено изнутри.

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

    Давайте объявим такой класс — Person, человек. И объявим три поля, и пометим их декоратором observable. Имя, фамилия и кличка.

    Когда мы говорим про MobX, очень полезно проводить аналогию с Excel.

    Observable-поля — это просто исходные данные в ячейках.

    Они позволяют остальным концепциям следить за изменением себя.

    Computed похож на observable тем, что также может уведомлять тех, кто на него подписан, о своем изменении, но при этом не хранит значения внутри себя, а вычисляет их на основе других observable-полей.

    В данном случае мы просто конкатинируем имя и фамилию через пробел.

    Если проводить аналогию с Excel, это ячейка с формулой. Кажется, пока все просто.

    Этот не тот action, который вы, наверное, знаете из Redux, но он очень похож.

    В терминах MobX action — просто некая функция. Здесь это метод, но это необязательно. Action не обязан быть методом класса, он может быть в любом месте приложения, главное, чтобы он был помечен декоратором action. Внутри этой функции вы можете изменять observable-поля, которые вы пометили ранее.

    Пока все понятно, метод устанавливает nickName.

    Теперь начинается магия.

    Самая главная концепция MobX — это реакции.

    Они похожи на computed, они тоже используют какие-то observable- или computed-поля внутри себя, но они не возвращают никакого значения. Вместо этого они дают побочный эффект.

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

    Самая простая реакция — функция autorun из библиотеки MobX.

    Напишем простой autorun, в который передается функция, просто выводящая некое выражение в консоль.

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

    Autorun, как только мы его вызываем, сразу первый раз запускает нашу функцию, которую мы передали в аргументе.

    При выполнении этой функции он обращается к observable-полям, в данном случае первым делом к nickName. Здесь срабатывает магия MobX: на самом деле, когда мы объявляли observable, вместо обычного поля был объявлен getter для этого поля.

    Когда мы обращаемся, observable-поле nickName у себя ставит инкремент: ага, у меня появился новый слушатель функции, которая завернута в autorun.

    Когда у меня что-то изменится, мне нужно этого слушателя уведомить об этом изменении. NickName пустой, поэтому дальше идет обращение к Person fullName. У нас происходит подписка на изменение этого поля. FullName является computed-полем, это getter, который внутри себя обращается к полям firstName и lastName.

    На этом выполнение функции заканчивается, и в этот момент MobX знает, что функция, которую мы передали в autorun, зависит от четырех полей: nickName, fullName, firstName, lastName.

    Дерево зависимостей выглядит так. Любое изменение observable-полей в первом столбце запустит заново выполнение autorun.

    Допустим, мы решили задать нашему человечку кличку Васек.

    Этот метод, который является action, совершает внутри себя операцию присваивания.

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

    Autorun получает уведомление, что что-то изменилось, надо заново перезапуститься. Запускает выполнение функции, обращается к полю nickName.

    На сей раз оно уже не пустое. На этом выполнение функции прекращается.

    Смотрите, как изменился список наблюдаемых полей. Поскольку мы обращались только к полю nickName, он остается в списке наших зависимостей. Все остальные три поля из списка зависимостей тоже вылетают. Если посмотреть на дерево, оно теперь выглядит так.

    До тех пор, пока не изменится nickName, autorun вообще будет игнорировать любые изменения полей firstName и lastName, потому что код устроен таким образом, что пока nickName не пустой, до поля fullName дело даже не дойдет никогда.

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

    Autorun — не единственный пример реакции. Есть реакция observer. Это helper для React.

    Если наш пример переписать в виде React-компонента, он будет выглядеть примерно так.

    Мы используем декоратор observer. Напомню: можно использовать обычные обертки здесь. Внутри метода render мы обращаемся сначала к nickName. Если он пустой, тогда уже к fullName. Ровно та же логика. Единственное, при использовании observer мы не выполняем функцию autorun, а вместо этого он при любом изменении полей, на которые мы подписаны, запускает переадресовку вашего компонента.

    Автоматическая подписка компонентов плюс observer позволяет кардинально минимизировать количество перерисовок React-компонентов.

    Есть часто наблюдаемый код, когда имеется какой-то флаг, который мы проверяем в самом начале метода render. Если он не выполняется, мы просто возвращаем null. Здесь очень помогает магия React. До тех пор, пока изменения у нас false, изменения любых полей, которые используются ниже, где написано много кода, observer будет игнорировать. Но как только флаг загорится, во время очередного перерендера он выполнит очередной код и подпишется на изменения полей, которые там используются.

    Если React экономит нам операции с домом, то MobX экономит нам операции с виртуальным домом. Чем меньше перерисовок даже в виртуальном доме, тем быстрее наше приложение.

    Расскажу об еще одной оптимизации, которая встроена в MobX, — кэшировании computed.

    Здесь наш fullName простой, но вообще они бывают и довольно сложные: какие-то фильтры, reduce, сложные вычисления. Возникает вопрос: если каждый раз обращаться к этому геттеру, не будет ли у нас излишнего выполнения всех этих операций, каждый раз мы будем одну и ту же операцию выполнять? Почему нельзя в кэш положить?

    До тех пор, пока данные, которые использует computed, не поменялись, computed первый раз выполняет свои вычисления, кладет значение в кэш, и каждый раз, когда к нему кто-то обращается, он отдает его сразу из кэша.

    Но если мы задаем поле nickName, и autorun отписывается от fullName, в этот момент fullName понимает, что у него больше не осталось подписчиков, выкидывает кэш, который потом собирается через garbage collector и работает просто как обычный геттер.

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

    Небольшой пример того, как можно работать с асинхронными данными при таком подходе.

    Можно руками запускать метод load, запускать флаг isLoading True или False, но у MobX есть такой хелпер, который называется fromPromise.

    Мы объявляем некое поле, заворачиваем асинхронную операцию в хелпер fromPromise, и в этом поле появляется два сабполя — state и value.

    В React-компоненте можно сначала проверять, что state pending. Тогда мы показываем какой-то loading. Если fullfilled, тогда обращаемся к полю value и рисуем наш компонент дальше.

    Итого, плюсы MobX.

    Уже слышу вопрос из зала. Я этого человечка называю Reduxman, это человек, который написал много кода на Redux. Какой вопрос он задает?


    А как же netability? Это что же, у вас можно методами прямо полями модели менять? Ну ни фига себе.

    А как же time travel? Мне же нужны не модели с методами, а простые plain JavaScript-объекты, чтобы можно было с их помощью делать undo, redo и прочие вкусные штуки.

    Как же мой любимый devtools, к которому я уже привык, чтобы можно было делать реплей действий, которые производил пользователь?

    Расскажу немного про Redux. Основные изменения, которые он произвел в головах разработчиков.

    Он отошел от ООП в сторону функционального программирования. Вместо моделей стали использоваться immutable-структуры. Вместо методов теперь есть экшены и редьюсеры. Вместо нормальных связей, ссылок между моделями теперь есть нормализация и селекторы.

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

    Когда я начал думать, чем Redux отличается от MobX, у меня возникла такая аналогия.



    Все любили этот мультик? А чем отличаются мультики, которые смотрит молодое поколение? Они вот такие.



    Знаете, в чем разница? «Том и Джерри» рисовали таким образом, брали кадры и каждый рисовали по отдельности.

    Ничего не напоминает? Immutable store в Redux-приложении. Каждый раз есть какой-то отпечаток, который мы руками конструируем, используем для этого библиотеку immutable или Object.assign или spread operator. Каждый раз мы дорисовываем руками состояние приложения на текущий момент. Если нужно откатиться, мы берем и обратно откатываем. Это все круто, только очень много кода получается. Я не люблю писать код, я люблю его удалять. Код — это зло. Самый быстрый код — это тот, который не выполняется.

    А новые мультики рисуют вот так.



    Рисуют трехмерную модель, программно ее поворачивают, берут кадр, поворачивают в другую сторону, берут кадр. Управляют живой моделью, и потом просто берут ее проекцию на экран.

    То, что мы руками пытаемся работать с immutable-данными, — это необязательно. Immutable-состояние нашего приложения — это еще один вид, еще одно представление, еще одно отображение. Можно использовать живую модель, просто каждый раз в любой момент времени получить его плоскую проекцию.

    Давайте покажу, как это сделать. Авторы MobX написали такую отдельную штуку. Это уже более opinionated-подход, который диктует вам, как писать приложение, но взамен дает много плюшек.

    Давайте напишем небольшой store, объявим класс Todo, для этого используется хелпер types, у которого есть метод model. Пока он пустой.

    Добавим title.

    Здесь мы объявляем, что это строка.

    Добавим опциональное булево поле isCompleted. Кстати, здесь есть возможность написать это короче. Если вы присваиваете какой-то примитив, то mobx-state-tree понимает, что это опциональное примитивное поле с дефолтным значением.

    Добавим reference. Это означает, что в folder будет лежать id какого-то другого объекта, но при создании модели mobx-state-tree по этому id достанет этот объект из некоего store и поставит его в этом поле. Пример я покажу чуть позже.


    Чтобы вся магия работала, нам нужно объявить класс Folder, у которого обязан быть id с типом types.identifier. Это как раз для того, чтобы связывать ссылки с объектами store по идентификатору.

    Объявим главный рутовый TodoStore, в котором будет два массива: todos и folders. Здесь можно видеть, как используется types.array, передаем в качестве аргумента класс, и MobX понимает, что это массив instance этого класса.

    Если мы объявляем геттер, он автоматически становится computed из терминологии MobX, как мы смотрели раньше. Здесь у меня есть геттер completedTodos, который просто возвращает список всех выполненных todo. Он кэшируется, и пока есть хоть один подписчик, он всегда возвращает закэшированное значение. Не бойтесь так писать, писать сложные выражения, все это будет закэшировано.

    Вот так создаются экшены. Первый объект в декларации — свойства и computed, во втором объекте перечислены экшены. Здесь и не надо их уже объявлять, mobx-state-tree по умолчанию считает, что все, что вы передаете вторым объектом, — экшены.

    Давайте попробуем создать store. У нас есть данные, допустим, они пришли с сервера, видите, они в нормализованном виде, у нас в folder лежит 1, а в списке folders есть объект с идентификатором 1.

    Создаем, используем.

    Первая строчка — все нормально, я использую поле title объекта todo.

    Во второй строчке уже магия: поскольку folder объявлен как reference, то MobX при создании модели автоматически, в первую очередь, положил folder в массив folders, а в моделях todo по ссылке, по идентификатору, добавил ссылку на этот объект. То, где в Redux мы бы писали селектор, здесь работает из коробки. Можно спокойно обращаться ко вложенным полям ваших ссылок, ваших референсов. И это работает, это очень удобно писать в компонентах без всяких селекторов и прочих map state to props.

    Мы собрали какую-то 3D-модель. Давайте попробуем запустить ее. Камера, мотор.

    Для начала попробуем получить обратно данные, которые мы в модель положили. Для этого есть хелпер getSnapshot. Передаем туда модель, получаем snapshot в виде обычного JS-объекта, как все редаксмены любят. Получил и получил, но у меня же модель постоянно меняется, как мне подписаться на изменения?

    Очень просто: есть хелпер onSnapshot, который позволяет подписаться на изменение любого поля в модели, при этом в качестве параметра он всегда передает новый snapshot, который он генерирует, но не просто так, иначе было бы глупо каждый раз новый объект генерировать. Он так же, как React, использует immutable.

    Если какие-то части менялись, он их реиспользует, запускает механизм structural sharing.

    Для изменившихся создает новые объекты.

    Как сделать time travel? Мы гуляем по истории и хотим какой-то snapshot применить к модели. Есть хелпер applySnapshot, передать модель, передать и snapshot. Он сравнивает то, что вы передали, и то, что сейчас в модели, берет диф и обновляет только те части, которые изменились.

    При этом он реиспользует модели, если у них совпадают идентификаторы. Если в модели лежит какой-то folders с id = 1, в snapshot тоже передается folders с id = 1. Он не пытается его перезатереть, а просто обновляет данные самого folder, если они изменились.

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

    Пожалуй, самая яркая иллюстрация того, как работают живые модели и snapshots.

    Есть живая модель, и мы в любой момент времени можем снять с нее snapshot.

    Наконец, бонус, специально для редаксменов. Есть для адаптера для работы с Redux. Если у вас уже есть большое приложение, написанное в Redux style, то вы можете переписать только store и из mobx-state-tree store получить reduxStore просто методом asReduxStore.

    Если вы привыкли работать с ReduxDevtools, можно просто использовать хелпер connectReduxDevtools, передать туда модель, store в виде mobx-state-tree, и все будет работать.

    Старые добрые ООП-модели вместо immutable-структур. Вообще-то, когда мы от них отказывались, кажется, мы выкинули ребенка вместе с водой. Они вообще-то были удобные, когда у вас есть данные и методы для работы с ними.

    Живые ссылки вместо селекторов. Вы можете вкладывать модели друг в друга сколько угодно, делать референсы и работать просто через точку. Todo.folder.parent и так далее, как хотите. При этом, когда вы будете сериализовывать, все будет автоматически обратно сериализовываться в нормализованный вид.

    Дешевое получение снэпшотов всего дерева с реиспользованием частей, которые не изменились. Применение снэпшотов с реконсайлингом, прямо как в React. Если объекты совпадают по идентификаторам, они будут реиспользованы. И — адаптеры для Redux Store и Redux Devtools.

    Как сказал Дэниел Эрвикер, MobX — это как React, только для данных. Здесь есть несколько ссылок, которые вы можете потом посмотреть:


    На этом спасибо.
    Яндекс 569,85
    Как мы делаем Яндекс
    Поделиться публикацией
    Комментарии 193
    • 0

      Остаётся только один вопрос: если у вас есть информация о непосредственных зависимостях любого состояния, а значит вы точно знаете каке узлы в дереве нуждаются в перерисовке, то на кой чёрт вам сдался этот реакт с его перегенерацией виртуального дерева с последующей реконциляцией и поиском узлов, которые нуждаются в перерисовке?

      • 0
        Я, как разработчик бизнес-логики и логики приложения, понятия не имею ни о каком дереве перерисовок. А разработчик презентационного компонента понятия не имеет ни о зависимостях состояния, ни о его изменениях.
        • 0

          Для всего этого виртуальный дом не нужен.

          • +1
            Может я не понял о чем Вы, но к примеру взять массив сданными. по которым строится список.
            В первый проход я построил список, а при следующих изменениях данных что? Если я каждый раз буду список создавать заново на уровне дом-дерева, то это не производительно. Получается что мне нужно написать свои функции, которые будут способны рендерить только изменения. Но я больше чем уверен, что разнообразность задач, поставит перед фактами, что одним только этим не обойтись. И в итоге так и придется писать свой аналог реакта или чего-то подобного. Поэтому проще взять готовое и к тому же будет проще найти специалистов, которые точно в Вашем самописе не разбираются.
            • 0
              Тут скорее речь о том, что на границе MobX и React теряется информация о зависимостях, которую рендерер мог бы использовать для точечного ререндинга прямо в DOM без построения нового, пускай и виртуального.
              • 0
                Не понимаю, как это? Вы имеете ввиду, что если mobx использовать без реакта, с обычными дом-элементами, то появляется возможность передать данные из стора в дом-элемент так, чтобы при обновлении стора точечно обновлялись дом-элементы? Разве такое возможно?
                • 0

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

                  • –1

                    Да, но если вас этих участков DOM изменилась тыща, то потребуется тыща обращений к DOM, а это — медленно.
                    React в этом случае всю тыщу изменений произведёт в виртуальном DOM, а в реальный сделает одну единственную вставку, что быстрее, т.к. в настоящий DOM будет всего одно обращение.

                    • 0

                      Да нет, Реакт сделает ровно столько же обращений к ДОМ, только ему для того, чтобы понять какие именно обращения к ДОМ необходимы — придётся делать сравнение виртуальных домов. Ну собственно, ваш пример с обновлением в 1000 местах — Ангуляр с точечными обновлениями уделывает Реакт в 2 раза.

                      • 0

                        Поможет ли здесь использование PureComponent?

                        • 0

                          Не поможет, ибо данные обновляются полностью.

                          • 0

                            Ок. Если данные обновляются полностью, то ReactJS перерендерит всё, от App и ниже в своём виртуальном DOM, а результат вставит на место #ROOT_APP сделав всего одно обращение к реальному DOM. Разве не так?

                            • +1

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

                              • 0

                                Вы точно разбираетесь в ReactJS?

                                • +1
                                  vintage прав. Если бы это было не так, вы бы наблюдали побочные эффекты, такие как: потеря фокуса, положения курсора ввода, сброс выделения контента, положения прокрутки внутри элементов и т.п.
                                  • 0
                                    KasperGreen, прошу прощения, проглядел. vintage не прав — по настоящим DOM-узлам Реакт, конечно, бегать не будет.

                                    Но и вы не правы — перерисовывать все Реакт тоже не будет, иначе это приведет к побочным эффектам, о которых я писал.

                                    Реакт просто сравнит старое и новое виртуальное дерево и применит изменения к DOM-дереву.
                                    • 0

                                      Не точно выразился, да. По настоящим узлам он в итоге пробежится и обновит ибо все данные меняются.

                                      • 0
                                        В общем, из того, что все данные изменились не следует, что изменилось все виртуальное дерево.
                                  • 0

                                    А вы сомневаетесь?

                        • +2
                          А каким образом так вышло, что mol-jsx работает вдвое быстрее, чем mol, и требует меньше памяти? Я думал, что mol-jsx — что-то вроде обертки над чистым mol, логично, что он должен работать медленнее?
                          • 0

                            Меня другое интересует. Как $mol умудряется в принципе обогнать Native DOM?

                            • 0

                              Очевидно за счёт ленивого рендеринга, который позволяет не только не рендерить то, что не изменилось, но и не рендерить то, что не видно.

                              • +1
                                То есть вариант на мол не быстрый, а просто ничего не делает. В чем тогда смысл такого сравнения? Что с чем сравнивается?

                                К слову, клик по блоку в мол тормозит сильнее всего. Ангуляр работает практически с той же скоростью, что и нейтив, реакт — посередине.
                                • 0

                                  Он далеко не "самый быстрый". Всё же ленивость и реактивность — не бесплатны. Но приложения на нём получаются самые отзывчивые. Именно потому, что он не делает то, что можно не делать. Но это синтетический бенчмарк. Вот более реалистичный.


                                  К слову, клик по блоку в мол тормозит сильнее всего

                                  Сомнительно. Может вы плавную анимацию фона посчитали за тормоза?

                                  • +1
                                    > Он далеко не «самый быстрый».

                                    Да он вообще не быстрый.

                                    Смотрите, этот тест направлен на то, чтобы оценить скорость рендеринга элементов фреймворком. Это предполагает, что элементы _будут_ рендериться. В результате можно оценить, как поведет себя фреймворк в ситуациях, которые сильно нагружают рендерер. Как ведет себя в таких ситуациях ваш фреймворк? Неизвестно, вы предпочли это скрыть.

                                    > Но приложения на нём получаются самые отзывчивые.

                                    Откуда это следует? Вы же тесты не хотите привести.

                                    > Сомнительно. Может вы плавную анимацию фона посчитали за тормоза?

                                    Не заметил никакой анимации.

                                    > Но это синтетический бенчмарк. Вот более реалистичный.

                                    Небось, там тоже все, что за пределами экрана — не рендерится, а по-этому оценить скорость работы мол нельзя?
                                    • 0

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

                                      • 0
                                        > Нет, он нацелен на то, чтобы оценить отзывчивость приложений.

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

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

                                        Замечательно. Как мне узнать, какое время будет между нажатием кнопки и отражением результата в $mol, когда 10к блоков будут видны на экране?

                                        Понимаете, случай с малым количеством блоков на экране никого не интересует — потому что он при ленивом рендеринге обрабатывается за время, неотличимое от мгновенного, на _всех_ фреймворках. И даже если ваш мол в данном случае дает задержку в 2мс вместо ангуляровских 5мс — какое кому до этого дело? Пользователь не увидит разницы.
                                        • 0
                                          А чтобы ее измерить — надо понять скорость рендера.

                                          Чтобы её измерить надо взять приложение, кликнуть и измерить через сколько времени закончится рендеринг. Абстрактная "скорость рендеринга" — не более чем бесполезные попугайчики.


                                          иначе разработчик фреймворка не прилагал бы таких усилий, чтобы скрыть результаты тестов

                                          Нет никакого секрета. Можете уменьшить объём данных, чтобы они все попадали в видимую область — тогда будет рендериться всё и $mol окажется в самом конце. Но важно даже не это, а то, что не зависимо от объёмов данных задержка не превышает 200мс.


                                          Как мне узнать, какое время будет между нажатием кнопки и отражением результата в $mol, когда 10к блоков будут видны на экране?

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


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

                                          Какой ещё фреймворк умеет в ленивый рендеринг? Именно фреймворк. То, что ручками вы можете накостылять ленивость куда угодно — с этим никто не спорит. Но это требует дополнительных телодвижений, которых никто не делает пока не начинает припекать.

                                          • +1
                                            > Но важно даже не это, а то, что не зависимо от объёмов данных задержка не превышает 200мс.

                                            Там сейчас задержка на клик порядка секунды при 10к блоков, а вы хотите сказать, что он за 200мс все перерисует?

                                            > Давайте вы перестанете фантазировать про 10к видимых блоков. Я уже объяснил, почему это не реалистично.

                                            Давайте вы не будете объявлять вполне обычные кейзы «нереалистичными», а просто скажете, как поведет себя $mol в этом случае и какая будет задержка?

                                            > То, что ручками вы можете накостылять ленивость куда угодно — с этим никто не спорит.

                                            Так а что еще надо, учитывая, что ленивость эта нужна в паре-тройке мест во всем приложении (а большинстве случаев и вовсе не нужна никогда)? А вот быстрый рендеринг — нужен практически всегда.
                                            • 0
                                              Там сейчас задержка на клик порядка секунды при 10к блоков, а вы хотите сказать, что он за 200мс все перерисует?

                                              Где "там"? Если вы про реализацию на $mol_dom_jsx, то там не используется ни реактивность, ни ленивость, ни реконциляция. Там дубовая реализация, аналогичная native dom. С той лишь разницей, что используется JSX для шалонизации.


                                              Давайте вы не будете объявлять вполне обычные кейзы «нереалистичными», а просто скажете, как поведет себя $mol в этом случае и какая будет задержка?

                                              Описание реалистичного кейса в студию.


                                              ленивость эта нужна в паре-тройке мест во всем приложении (а большинстве случаев и вовсе не нужна никогда)?

                                              Описание кейса, когда ленивость является лишней, в студию.

                                              • 0
                                                > Где «там»? Если вы про реализацию на $mol_dom_jsx

                                                Я про обычный $mol.

                                                > Описание кейса, когда ленивость является лишней, в студию.

                                                Проще указать, когда является — если вам надо вывести какой-нибудь список на много-много позиций, и пагинация в данном конкретном случае плоха. Во всех остальных кейзах — ленивость является лишней и вредной.

                                                > Описание реалистичного кейса в студию.

                                                Я же вам указал — грид. Только не с пустыми ячейками, а с контентом.
                                                • 0
                                                  Я про обычный $mol.

                                                  И как вы этого добились?


                                                  Во всех остальных кейзах — ленивость является лишней и вредной.

                                                  Обоснуйте.

                                                  • 0
                                                    > И как вы этого добились?

                                                    Кликнул по блоку. Выше же говорил об этом уже, клики мол обрабатывает медленнее всех.

                                                    > Обоснуйте

                                                    Никакой пользы она не приносит, а накладные расходы — остаются.
                                                    • 0
                                                      Кликнул по блоку. Выше же говорил об этом уже, клики мол обрабатывает медленнее всех.

                                                      Не наблюдаю у себя ни в одном браузере. Поможете воспроизвести?


                                                      Никакой пользы она не приносит, а накладные расходы — остаются.

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

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

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

                                                        > Не наблюдаю у себя ни в одном браузере. Поможете воспроизвести?

                                                        Первый приведенный вами бенчмарк, ставим 10к блоков, мотаем на середину, выделяем один блок, потом выделяем соседний, первый блок гаснет где-то через полсекунды-секунду. Никакой анимации при этом не видно (анимация есть при наведении на блок)
                                                        • 0
                                                          Так у вас и без ленивой загрузки будет такая же высокая отзывчивость и плавные анимации.

                                                          Ну конечно, делаем больше работы, а отзывчивость не проседает.


                                                          Первый приведенный вами бенчмарк, ставим 10к блоков, мотаем на середину, выделяем один блок, потом выделяем соседний, первый блок гаснет где-то через полсекунды-секунду.

                                                          Тут было две причины:


                                                          1. Не кешировалось свойство row_selected, из-за чего все 10к элементов обновляли свои атрибуты при изменении текущего элемента. Поправил.
                                                          2. Бага в хроме вызывает адские фризы при клике. Тоже поправил.

                                                          Теперь не тормозит. Спасибо :-)


                                                          Заодно обновил Реакт до 16 версии. Первичный рендеринг замедлился, зато обновление ускорилось.

                                                          • 0
                                                            > Ну конечно, делаем больше работы, а отзывчивость не проседает.

                                                            Да нет, просто дело в том, что нету реальной разницы между 100фпс и 200фпс.
                                                            • 0

                                                              Открываем то же самое на мобилке и видим разницу между 10фпс и 20фпс.

                                            • +1

                                              Скажите, а ваш мега-ленивый $mol поисковыми роботами вообще индексируется? А Ctrl+F на ленивой странице работает?

                                              • 0
                                                > А Ctrl+F на ленивой странице работает?

                                                Уже обсуждали. Вместо ctrl+f предлагается пользоваться специальным костыле-поиском на странице.
                                                • –1

                                                  Для поисковых роботов всё нелениво рендерится на сервере.


                                                  Ctrl+F очевидно не найдёт то, что не отрендерено. Также он не найдёт то, в принципе не выводится (идентификаторы сущностей, например) или выводится в графическом формате (статусы, альтернативный текст картинок). И наоборот он найдёт кучу лишнего (например, каку-нибудь ерунду из рекламного блока в сайдбаре).


                                                  Можете провести юзабилити тестирование и посмотреть что выберут пользователи:


                                                  1. Долгое открытие страницы, зато потом можно что-то найти по ctrl+f.
                                                  2. Быстрое открытие страницы, но ctrl+f находит не всё.
                                                  3. Быстрое открытие страницы, на котором есть поле фильтрации, которое позволяет отсеить нерелевантные данные.
                                                  4. Долгое открытие страницы, на котором есть поле фильтрации, которое позволяет отсеить нерелевантные данные, но и можно поискать по ctrl+f.
                                                  • 0
                                                    Пользователь выберет быстрое открытие страницы с ctrl+f и полем фильтрации.
                                                    • –1

                                                      Физическая реальность, к сожалению, такое не позволяет в общем случае.

                                                      • 0
                                                        Дмитрий, а если рендер сделать не ленивым, а отложенным, с RAF-синхронизацией? Тогда мы будем иметь:

                                                        • быстрый начальный рендер страницы (данные вьюпорта рендерятся сразу же, остальное отложенно)
                                                        • быстрый рендер по клику при открытии (т.к. нет ленивого рендера)
                                                        • поиск по Ctrl+F
                                                        • поле фильтрации

                                                        Ну только не говорите мне, что это невозможно реализовать.
                                                        • 0

                                                          Это более чем возможно. Но опять же:


                                                          1. Пока не закончится рендеринг всего, ctrl+f не будет полноценно работать. А асинхронный рендеринг займёт куда больше времени, чем рендеринг сразу всего. Так что ctrl+f будет работать непредсказуемо для пользователя, что ещё хуже, чем просто работает / не работает.
                                                          2. Пока идёт рендеринг того, что пользователь не видит — будут наблюдаться подтормаживания при работе с тем, что он видит. Поток всего один, а превысить 16мс — легко.
                                                          3. Далеко не факт, что пользователь пойдёт потом мотать скролл до самого конца. В этом случае мы жрём батарейку впустую ради… возможности искать по ctrl+f?
                                                          4. Чем больше элементов в ДОМ, тем медленнее происходит reflow, repaint и прочее.
                                                          5. Чем больше мы нарендерили, тем больше потребуется видео-памяти (пропорционально площади всего контента в пикселях), которой на мобилках — кот наплакал.
                                                          • 0
                                                            1 и 2 — React Fibers создавались как раз с этой целью.

                                                            3 — а вот для реализации этого юзкейза я участвовал в разработке подобного, но для html5 canvas, а не DOM. Так что, как видите, это не просто факт, а достаточно факт, чтобы превратиться в требование от стейкхолдера.

                                                            4 — для этого опять же существует vDOM. Например React.

                                                            5 — никуда не деться и я даже не знаю, что хуже: отсутствие привычных хоткеев или прожорливое приложение. 4-5 лет назад это было критическим пунктом, но не уверен, что это сейчас имеет смысл.

                                                            Пятый пункт — единственный, где ещё могут возникнуть вопросы. По остальным пунктам — это ответ на ваш самый первый комментарий к этой статье.
                                                            • 0
                                                              У файбера есть и недостатки: плавность, ценой пропуска кадров. Это проявляется, если проц слабый или загружен. Откройте 10 демок на файбере и в одной повозите мышкой над цифрами. Проблему производительности он скорее маскирует, чем решает. Может быть когда-нибудь в многопоточной среде и будет решать, но не в однопоточной.

                                                              Про скорость repaint в DOM, мне кажется, Дмитрий имел в виду, что если на странице много элементов, в том числе и за пределами видимой области, то это будет тормозить. И vDOM тут никак не поможет.

                                                              vDOM не бесплатный, поэтому есть shouldComponentUpdate, отчасти setState, а также много попыток написать быстрее, чем в react (тот же inferno или ivi).

                                                              vDOM — это оптимизация внизу, перед выводом в браузер. А mobx-подобные решения — это оптимизация наверху, сразу после изменения данных. Что может быть гораздо быстрее любого vDOM, т.к. глубина вычислений будет меньше в среднем случае.

                                                              Многие фреймворки добиваются высокой производительности, используя точечные обновления, вместо vDOM (тот же angular4, отчасти vue). Интересно наблюдать, как развивается этот альтернативный подход, в том числе и в mol.
                                                              • +1
                                                                Да, маскирует, но это как раз и является целью. Нам и нужно отложенно отрендерить большой контент без фризов и с быстрым начальным рендером.

                                                                Если он имел ввиду это, то у нас юзкейзы с гридом, а браузеры уже научились оптимизировать paint и отбрасывать невидимые элементы. Слабо себе представляю пример, чтобы на экране были одновременно все дивы видимых грида, кроме выдуманного где-то здесь использования дивов в качестве пикселей.

                                                                vDOM не бесплатный, конечно же, но он быстрей, чем DOM. И его тоже надо уметь готовить. А чтобы использовать Inferno, всё равно надо соблюдать определённые соглашения и рекомендации. Итого получается то же самое, что и с ручной оптимизацией в узких местах. Доводилось слушать автора Inferno на конференции.

                                                                Мне тоже интересно, конечно же. Истоки этого обсуждения в том, что автор мола хочет объяснить, что React не нужен. Пока не объяснил)
                                                                • 0
                                                                  Всегда ли плавность важнее гарантированного отклика? Да и почему, собственно, понадобилось добавлять какой-то Fiber для видимости ускорения? Для меня убедительно выглядит пример треугольника на mol, который обеспечивает плавность без технологий, вроде Fiber. И для этого надо раз в 5 меньше кода и это все написал один человек, а не команда из гиганта FB.

                                                                  Используя 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
                                                                  • +1
                                                                    Гарантированный отклик важнее, конечно же. Именно поэтому приоритет операций от пользователя максимальный: см. SynchronousPriority.

                                                                    Вопросом про нужность 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-й.
                                                                    • +1
                                                                      После отправки данных в пропсы 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 запилил свой файбер, но он наверное опять скажет «не нужно».
                                                                      • 0

                                                                        Да я пилил уже квантификацию вычислений, причём на уровне $mol_atom а не $mol_view. Но это даёт артефакты в духе "во второй половине страницы ничего не обновляется, когда в первой что-то постоянно обновляется" и тому подобные, так что пока отказался. Надо будет ещё подход сделать — может получится нормально реализовать.

                                                                        • 0
                                                                          Я к тому, что mol по сравнению с реактом без файбера выдавал лучшую производительность, сравнимую с реактом с файбером на одной вкладке, да.
                                                                          Это не так. Прочитайте вот этот комментарий Дмитрия. Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.

                                                                          … автоматически ускорить рендеринг и вдруг, для некоторых случаев… В vue как-то без shouldComponentUpdate обходятся.
                                                                          Ну мы же выяснили, что shallowCompare (которое pure) есть и в реакте, мобиксовское тут ничего не добавляет. А обходятся потому что автор решил, что это не нужно. «Вам это не нужно, попробуйте организовать компоненты по-другому.» :) В моём текущем проекте всего два компонента имеют shouldComponentUpdate с кастомной функцией, остальным это не нужно. Два — это всё равно что ничего. Во Vue со второй версии тоже есть vDOM, и есть shallowCompare, но все равно есть проблемы с производительностью, к которым подойдёт ручная настройка.

                                                                          Можно было бы оставить только pure-компоненты, зоопарк способов менять состояние свелся бы к единообразному подходу на основе mobx и его надстройках. Все были бы счастливы.
                                                                          Я счастлив, я меняю состояние компонентов через пропсы, и далеко не всё храню в глобальном сторе. И не использую зоопарк. А подход, который вы описали, на самом деле приводит к ещё одному конкурирующему стандарту.
                                                                          One more competing standard
                                                                          image

                                                                          Почему не с redux, потому что это opinionated-подход, навязывающий определенный архитектурный стиль.
                                                                          Не понял, почему? Вот у меня все компоненты берут всё из пропсов. Коннект — это просто один из внешних хоков (HOC). Они не знают ничего про ридакс и замечательно будут работать и с MobX и с реализацией флюкса на Backbone. И функциональные и обычные class-based компоненты.

                                                                          Тут не понял. Как vDOM может уберечь от фризов? Что такого было бы в приложении с mobx и нативном DOM, что фризило бы рендеринг?
                                                                          Мы не совсем про vDOM говорим, а про ненужный реакт. Фризит начальный рендер, большое обновление данных за раз. Мол тут как раз замечательный пример: там используется ленивый рендер и отсечение элементов, не попадающих во вьюпорт. Если попытаться после рендера прокрутить вниз большой список, то появляются дикие фризы: мол пытается отрендерить сразу большое количество элементов (прокрутившихся мимо вьюпорта) за раз, да ещё и все вместе, никак их не кооперируя.

                                                                          Если я правильно вас понял, вы считаете, что все DOM-элементы в компоненте будут пересозданы, если какое-либо его observable-свойство поменялось.
                                                                          Так реализация прослойки между mobx и DOM должна точечно мутировать DOM-ноду, если надо поменять одно из ее свойств или дочернюю ноду, как это и делает mol_jsx.
                                                                          Да, так, будут. Нет, не так, mol_jsx их пересоздаёт.
                                                                          • 0
                                                                            Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.

                                                                            Вы бы не говорили глупости, а проверили сперва сами. В этой демке $mol рендерит все кружочки, даже если они не влезают в экран.

                                                                            • 0
                                                                              А в демке на прокрутку отбрасываются невидимые дивы :) И это никакой не $mol_view, а что-то модифицированное?)
                                                                              • 0

                                                                                Ох, давайте я расскажу вам как сейчас работает ленивый рендеринг в $mol_view:


                                                                                1. У каждого компонента есть свойство minimal_height. Рассчитывается оно эвристически на основе содержимого.
                                                                                2. Есть компонент, который управляет скроллингом — $mol_scroll — он предоставляет информацию о нижнем крае видимой области.
                                                                                3. Есть компоненты-лейауты. Например, используемый в "демке на прокрутку" вертикальный лейаут — $mol_list. Он умеет использовать информацию о минимальном размере вложенных в него компонент и нижнюю границу скроллинга, чтобы отсечь те компоненты, которые точно не попадают в видимую область. Так как minimal_height как правило не соответствует реальным размерам компонент (они больше), то $mol_list обычно рендерит чуть дальше видимой области.

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

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

                                                                                    Не обманываем. Там замеряются 2 основных параметра, важных для любой страницы — время показа и время обновления данных. Всё это с точки зрения пользователя.


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

                                                                                    Во всех реализациях написан идиоматичный для фреймворка код.

                                                                            • 0
                                                                              Модифицируйте пример с треугольником Серпинского так, чтобы все кружочки всегда попадали на экран — мол очень сильно просядет.
                                                                              Пока не понял что модифицировать, вроде столько же кружочков, у меня не тормозит.

                                                                              Во 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-шника нет. Судя по реализации там есть переспользование и в примере видно, что не пересоздается нода. Может мы о разных нодах говорим?
                                                                              • –1
                                                                                Пока не понял что модифицировать, вроде столько же кружочков, у меня не тормозит.
                                                                                Я смотрел на оригинальную демонстрацию, а не на его, и вот там нет скейла .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-адаптер, о чём честно и написано.
                                                                                • +1
                                                                                  Вот ещё прямой ответ, что не нужен shouldComponentUpdate… потому что не нужен.

                                                                                  Не нужен. Там человек пытался вырывать гланды через анус — ему объяснили, что так делать не правильно. Ему надо было чтобы содержимое не обновлялось, пока его редактируют, а shouldComponentUpdate вырубил бы вообще все обновления (даже атрибутов и свойств).


                                                                                  Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?

                                                                                  Там другие обсёрваблы.

                                                                                  • +1
                                                                                    > У нас нет setState, только хоки и ридакс, в сторе лежит минимум данных — только те, которые нужны для апи и некоторые глобальные производные.

                                                                                    А где хранится стейт компонент?

                                                                                    > Почему же реакт был сделан на чистом листе?

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

                                                                                    > Ну вот в Ангуляре есть те же самые обсёрваблы. Что-то там не очень упростилось. В чём подвох?

                                                                                    В том, что обсерваблы в ангуляре для апдейта дом не используются, так что нет, не «в ангуляре те же самые обсерваблы».
                                                                                    • 0
                                                                                      А где хранится стейт компонент?
                                                                                      Используем recompose, выносящий хранение стейта в хоки. Благодаря этому компонент просто получает пропсы и не знает, откуда они приходят.

                                                                                      Потому что стояла задача портирования существующего пхп кода со вполне определенной архитектурой (принимаем запрос с параметрами — рендерим по этим параметрам страницу). На обсерваблы и компьютеды это не ложится, зато ложится на чистые компоненты.
                                                                                      Это был вопрос, на который я не ждал ответа) Моя цель была показать, что Mobx-подобные подходы уже были, и что они не повлияли на реакт (а может и повлияли, но в обратном ключе: показали, как не надо делать).

                                                                                      А про саму суть вопроса — я читал немного другое: что цель стояла как раз сделать подход, подобный XHP (автоматическая обработка xml-like синтаксиса для безопасности), для работы с динамическими приложениями в браузере. И что на реакт повлиял Clojure и создание Om как обёртки реакта (на CloseScript), что привело и к зависимости реконсиляции от иммутабельных структур.

                                                                                      Т.е. дело не в наследии от PHP, а в наличии адептов функционального программирования.
                                                                                      • 0
                                                                                        > Используем recompose, выносящий хранение стейта в хоки. Благодаря этому компонент просто получает пропсы и не знает, откуда они приходят.

                                                                                        То есть внутренный компонент может менять внешний стейит из НОС?

                                                                                        > Моя цель была показать, что Mobx-подобные подходы уже были, и что они не повлияли на реакт (а может и повлияли, но в обратном ключе: показали, как не надо делать).

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

                                                                                        > Т.е. дело не в наследии от PHP,

                                                                                        Прямо по вашей ссылке:

                                                                                        > ReactJS started as a JavaScript port of XHP, a version of PHP which Facebook released four years ago.

                                                                                        > а в наличии адептов функционального программирования.

                                                                                        Уже это обсуждалось. В реакте функционального программирования не больше, чем в любом другом фреймворке. Ну и вообще странно ожидать от кондовых пехепешников из фейсбука любви к функциональным подходам :)
                                                                                        • 0
                                                                                          То есть внутренный компонент может менять внешний стейит из НОС?
                                                                                          Внутренний компонент получает функцию, которую повесит на onClick, и строку, которую положит в значение спана. Он не знает, откуда эти функция и строка пришли — от ридакса или от хока.
                                                                                    • 0
                                                                                      Но они не забыли про ручную оптимизацию)
                                                                                      Это как повернуть. На мой взгляд это следствие того, что 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.
                                                                                      Вывод типов в redux connect
                                                                                      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)
                                                                                      
                                                                                      flow/try

                                                                                      Возможно я нахожусь в каком-то локальном экстремуме, но я совершенно не вижу, как можно большой объём переиспользуемой логики (продукт — личный кабинет, высокодинамичный 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 и пытается его продать. В этом есть рациональное зерно.
                                                                                      • 0
                                                                                        Много, что б было меньше, не могли бы вы привести пример, как вы готовите редакс с сагой? Если на 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. Предлагаю либо закругляться, либо в личку перейти)
                                                                                        • 0
                                                                                          Не очень понял разницу «редакс или экшен/редьюсер».
                                                                                          Да, я ошибся, имелось в виду setState или экшен/редьюссер. Но теперь я понял про recompose с withState. Для меня важен подход, когда есть прозрачность интерфейсов, везде и точно работает выведение типов без костылей вроде utility types. Когда типизация помогает, а не мешает, когда есть единообразие и простота реализации SOLID (хотя бы SO) — это индикатор того, что путь верный. Как только появляются строки для работы со стейтом, возникают затруднение с выведением — это плохой признак.

                                                                                          По мне, даже setState был бы лучше этого. Напомитает подход, который был в React.createClass, описание класса через фабрику, нестандартную спецификацию библиотеки.

                                                                                          Даже если забить на выведение типов, которое тут никогда не заработает, вы действительно считаете, что вот это

                                                                                          Пример на compose
                                                                                          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);
                                                                                          


                                                                                          Проще и понятнее, чем это?

                                                                                          Пример на setState
                                                                                          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
                                                                                              })
                                                                                            }
                                                                                          }
                                                                                          


                                                                                          Если уж пытаться добавлять сахар, то по мне лучше так:

                                                                                          Пример на reactive-di
                                                                                          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-стек: скрытые угрозы», Илья Климов
                                                                                          .

                                                                                          Предлагаю либо закругляться, либо в личку перейти)
                                                                                          Да, давайте прервемся и как-нибудь в другой раз продолжим. Слишком много направлений, в комментах тяжко это обсуждать.
                                                                              • 0
                                                                                я не хочу выделять каждый див в отдельный компонент

                                                                                Забавно, что именно в Реакте так в основном и делают.
                                                                                А что вас смущает в том, что каждый див — отдельный компонент? $mol_view компонент — не более чем легковесная реактивная обёртка над дом-элементом.


                                                                                я не хочу, чтобы список товаров умирал через 5 секунд после начала прокрутки.

                                                                                Вы хотите, чтобы он умирал сразу, если программист не позаботился о паджинации?


                                                                                Прозводительность этой «наколеночной» замены vDOM (как и исходная реализация мола) сравнима с React только при совсем тепличных условиях, любое реальное приложение выходит за рамки этих условий.

                                                                                Пример "выхода за рамки условий" приведёте? И, кстати, что за условия?

                                                                                • +1
                                                                                  «Так все делают» — это плохой аргумент. Я так не делаю. Мои знакомые так не делают.

                                                                                  Нет, не хочу. И тут мы прошли по кругу и вернулись к началу. А говорили про отложенный (не ленивый!) рендер.

                                                                                  Под условиями я имел ввиду выход за рамки маленькой демки. Вы сами ниже с горем пополам согласились, что дивы будут пересоздаваться, если явно не указать id-шники им. И никакие обсёрваблы этому не помогут (хотя да, помогут, если каждый див вынести в отдельный mol_view). Это не говоря про то, что должно быть маленькое приложение, а чем оно больше разрастается — тем больше становится запутанными взаимосвязи между вьюшками-дивами. Отлаживать это всё добро, да ещё и со странными стектрейсами — то ещё удовольствие. Объединение компонентов ожидаемо приводит к падению производительности. Ибо вся магия в ручном разделении.
                                                                                  • –1
                                                                                    Вы сами ниже с горем пополам согласились, что дивы будут пересоздаваться, если явно не указать id-шники им.

                                                                                    Вообще-то я сразу подчеркнул, что дом-узлы в случае $mol_dom_jsx реиспользуются по идентификаторам. И это лишь пример jsx рендерера без vdom. Можете написать свой с автогенерацией идентификаторов в духе реактовых "1.3.2.1.2". Но я за человекопонятные идентификаторы.


                                                                                    $mol_view же хоть и добавляет идентификаторы, но не зависит от них. Там каждый компонент сам создаёт себе узел и кеширует его у себя.


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

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


                                                                                    Отлаживать это всё добро, да ещё и со странными стектрейсами — то ещё удовольствие.

                                                                                    Что странного в наших стектрейсах?


                                                                                    Объединение компонентов ожидаемо приводит к падению производительности.

                                                                                    Любой компонент является композицией других компонент. Ни к какому падению производительности это не приводит.

                                                                                    • 0
                                                                                      Можете написать свой с автогенерацией идентификаторов
                                                                                      Вы просили указать — я указал. Если писать полноценный vDOM, то он превратится в (P)React. Что и требовалось доказать. Ваше залихвастское утверждение «да я тут за 5 минут на коленке написал ваш ненужный на самом деле вдом» не подтвердилось.
                                                                                      весьма наглядно представлены
                                                                                      Очень интересно, когда вы агрессивно рекламируете свою библиотеку, но при этом напрочь игнорируете чужое мнение и напропалую используете демагогические приёмы. Нет, tree не нагляден. Нет, я говорил не про демки. Давайте начнём хотя бы с 10k sloc.
                                                                                      • 0
                                                                                        Если писать полноценный vDOM, то он превратится в (P)React.

                                                                                        Не нужно писать vDOM.


                                                                                        вы агрессивно рекламируете свою библиотеку

                                                                                        Процитируйте, пожалуйста, "агрессивную рекламу".


                                                                                        напрочь игнорируете чужое мнение и напропалую используете демагогические приёмы

                                                                                        А вам не приходило в голову, что ваше мнение может оказаться неверным, и люди не соглашаются с вами не потому, что они такие упёртые бараны?


                                                                                        Нет, tree не нагляден.

                                                                                        Обоснуйте.


                                                                                        Давайте начнём хотя бы с 10k sloc.

                                                                                        http://mol.js.org — 25ksloc

                                                                                        • 0

                                                                                          vintage яндекс уже предлагал вам эквайринг или они пока размышляют?:))) Tree очень неплох, но jsonnet мне ближе. Кстати, ждем reActive Tree c проприетарным кодом в нодах.

                                                                                          • 0

                                                                                            У Яндекса своя вязанка велосипедов: bemhtml, bemjson, bemtree и другие.


                                                                                            Кстати, ждем reActive Tree c проприетарным кодом в нодах.

                                                                                            Это что такое?

                                                                                          • 0
                                                                                            Тоже не понимаю, зачем вы его написали :) Вы совершенно не собирались достигать заявленных целей написания mol_jsx, вместо этого с радостью окунулись в демагогию.

                                                                                            люди не соглашаются с вами
                                                                                            Моё мнение?) Мне совершенно не интересно, соглашаетесь вы или нет со мной. Вообще-то это я с вами не соглашаюсь и вместе с другими порасспрашивал про то, что вы понаписали. Расспросил, спасибо. Теперь мне более понятно, что вы понаписали, поэтому вы перешли к упёртым баранам и точкам зрения :)

                                                                                            Что обосновать? Я так думаю, мне он просто не нравится (как и coffescript, например). Я знаю ваш ответ, он предсказуем: вот он, за два года не утратил актуальность, вы по-прежнему так отвечаете всем, кто с вами не согласен :) И да, это с вами люди в большинстве своём не соглашаются, судя по ссылке выше, по этому посту и так далее. И вы зря считаете, что это потому, что вы никем не понятый одиночка, ваяющий бриллиант во тьме невежества. Vue.js успел стать популярным, хотя был написан одиночкой (и он до сих пор поддерживается практически только им), а заматерев, перешёл на ненавистный вами vDOM.

                                                                                            mol.js.org — 25ksloc
                                                                                            Вот точка входа, судя по всему? А рядышком в meta.tree инклуды, где 6 десятков других tree-файлов (которые кушаются вместе с .ts). Даже с учётом простой плоской структуры сего одностраничника выглядит крайне неудобно и малопонятно где какие компоненты используются. Не говоря уж про то что совершенно не ясно, как это всё работает с ts. Эта демонстрация получилась ещё более неудачной.

                                                                                            P.S. Вы очень зря постоянно пытаетесь вести себя в таком стиле, это отталкивает :) И совершенно зря, указывая на различия во мнениях, считаете их закостенением и/или конформизмом. Этим вы ещё более отталкиваете и собеседника и читателей. Мы все не первый год живём, некоторые даже успели повзрослеть, много раз меняли мнения на разные вещи. Не говоря уж про программирование: мы все наблюдали, как меняются языки, подходы и инструменты. И менялись вместе с ними.
                                                                                            • 0
                                                                                              Вот точка входа, судя по всему?

                                                                                              Нет, точка входа тут: 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 не нагляден.

                                                                                              … потому что...


                                                                                              мне он просто не нравится

                                                                                              Чудесно.

                                                                              • 0

                                                                                Ну смотрите, когда водишь мышью над кругами — на всей странице перестают обновляться числа. Считаете ли вы это нормальным?



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


                                                                                Ну и собственно, можете заглянуть в профайлер. (Кстати, чтобы найти этот комментарий мне пришлось воспользоваться гуглом, ибо ни ctrl+f ни паджинация мне никак не помогли со страницей всех моих коментариев.)

                                                                                • 0
                                                                                  Неубедительная аргументация, если честно. Артефакт может зависеть от загрузки процессора, версии файбера и расстановки приоритетов компонентам.

                                                                                  Во-первых, надо уточнить случаи, когда
                                                                                  рендеринг каждого кадра важнее скачков анимации и доказать, что это так.

                                                                                  Во-вторых, «полное отсутствие обновления» — экстраполяция. Лучше уточнять параметры. Т.е. на слабых мобилках, при неправильно расставленных приоритетах может и да, ваш способ лучше, но это пока не делает файбер костылем.
                                                                                  • 0
                                                                                    Не хочу вас огорчать, но на моём домашнем компьютере $mol фризится (не тормозит, а именно фриз на некоторое время) при обновлении чисел, в то время как React работает адекватно.
                                                                                    • 0

                                                                                      Да с чёго бы мне расстраиваться? Адекватно — понятие относительное. Ну вот я замедлил свой комп в 5 раз:


                                                                                      • React15 — 1 фпс
                                                                                      • React16 — 60 фпс, числа не обновляются вообще
                                                                                      • $mol — 60 фпс, фризы на 150мс при обновлении чисел

                                                                                      Кстати, я тут заметил, что $mol выдавал низкий фпс (13) в этом тесте между обновлениями чисел. Причин опять оказалось 2:


                                                                                      1. $mol_view актуализирует всё состояние дом-узла, когда что-то меняется в данных. Тут точки рендерились прямо в корень, что приводило к тому, что при изменении стиля корневого элемента $mol_view проверял так же и в правильном ли порядке расположены точки. Решение — поместить точки во вложенный элемент. В Реакте, если что, сделано так же.


                                                                                      2. Использование touch-action отличный от auto приводит хром к безумно долгому обновлению дерева слоёв. Так что не стоит его использовать без крайней необходимости.
                                                                                      • 0
                                                                                        Странно, Core i7-4550U 1.50GHz и 61-м хроме на убунте

                                                                                        10 вкладок с треугольником на mol летают (видимо я уже обновленную версию открыл), грузят где-то 10%, а на реакте под 100% все ядра грузят и вычисления стоят на одной цифре, правда анимация наиплавнейшая.

                                                                                        У вас, я так понимаю срабатывает трюк: если вкладка не видна, вычисления засыпают.
                                                                                        • 0

                                                                                          У меня Реакт тоже пессимизируется в фоновых вкладках, правда не сразу, а через несколько секунд. Думаю тот дело в том, что $mol_atom все вычисления откладывает в requestAnimationFrame, который перестаёт работать сразу при скрытии вкладки. В Реакте же вычисления инициируются сразу в обработчике setTimeout а в requestAnimationFrame откладываются лишь, через 10мс работы. А setTimeout в фоновых вкладках пессимизируется не так радикально, как requestAnimationFrame.

                                                                                    • 0
                                                                                      Ну смотрите, когда водишь мышью над кругами — на всей странице перестают обновляться числа. Считаете ли вы это нормальным?

                                                                                      Конечно считаю ненормальным. Но вы обманываете сами себя, в реакте нет такого. Эта демка была сделана давно и на нестабильной ранней имплементации.

                                                                                      Сейчас не надо оборачивать setState в ReactDOMFiber.unstable_deferredUpdates, как и самого ReactDOMFiber не стало.

                                                                                      Ещё один обман: это не бенчмарк, а сравнение плавности анимации в реакт15 и в реакт16. Ну и да, присоединюсь к людям выше, у меня тоже мол намного медленней работает, чем вы рассказываете.
                                                                                      • 0

                                                                                        Я попробовал подключить версию 16.0.0 — получил 2.5 фпс. Ну да, в 3 раза быстрее React15 :-)
                                                                                        Если вы знаете как сделать правильно, чтоб не тормозило — сделайте пул-реквес (https://github.com/nin-jin/sierpinski), а не рассказывайте, что я сам себя обманываю.

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

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

                                                                                          А пример с серпинским устаревшим выглядит, других пока нет, к сожалению.
                                                                                          • +1
                                                                                            vintage, что вы хотите достичь? Этот треугольник серпинского — заведомо сложный пример для реакта, как и для любой библиотеки, которая не отсекает сама дивы за краем экрана. А в каждый кружок специально добавлен долгий цикл на 80 миллисекунд. Браузер, конечно, отменяет прорисовку дивов вне вьюпорта, но эти вычисления обходятся куда дороже, чем вывод этого маленького дива.

                                                                                            Раз уж вы хотите померяться письками с реактом, то вот вам демка: build-mbfootjxoo.now.sh. В js указан sourcemap, так что исходники там есть (понятия не имею, есть ли оригинальный репозиторий в открытом доступе).

                                                                                            Треугольник серпинского для актуальной версии мне тоже интересно будет сделать. Но не вижу никакого смысла добавлять его в ваши «бенчмарки» :)
                                                                                            • 0
                                                                                              Этот треугольник серпинского — заведомо сложный пример для реакта

                                                                                              Который был написан самими фейсбуковцами для рекламы файбера. Это не "мои бенчмарки".


                                                                                              как и для любой библиотеки, которая не отсекает сама дивы за краем экрана

                                                                                              Это демо вообще не про отсечение. Если у вас треугольник не влезает в экран — просто уменьшите зум.


                                                                                              А в каждый кружок специально добавлен долгий цикл на 80 миллисекунд.

                                                                                              В том-то и дело, что не в каждый кружок, а в каждую группу кружков/подгрупп и не 80мс, а по 0.8мс на группу. Именно поэтому реакт и тормозит, что ему чтобы обновить значения кружков нужно пересчитать все группы, а это долго. Использование реактивного программирования позволяет не пересчитывать группы на каждый чих. Поэтому $mol обновляет содержимое кружков за 35мс, а Реакт — за 385мс. А всё, что Делает РеактФайбер — размазывает эти 385мс на 40 кадров.

                                                                                              • 0
                                                                                                Который был написан самими фейсбуковцами для рекламы файбера. Это не «мои бенчмарки».

                                                                                                Анимацию он хорошо показывает по-сравнению со старым реактом и производит вау-эффект, для этого наверное и создавался. Примеров мало, что б делать выводы какие-то.
                                                                                                • 0
                                                                                                  Который был написан самими фейсбуковцами для рекламы файбера. Это не «мои бенчмарки».
                                                                                                  Не знаю, что вы себе напридумывали, продолжая обманывать себя, но он не написан для рекламы файбера. Он написан для того, чтобы показать разницу между двумя версиями реакта. Если бы они рекламировали скорость работы рендера, то сделали бы летающих по странице 10 тысяч пикачу.

                                                                                                  Точно, 0.8 мс) Но нет, реакт не тормозит тут. Это ненормальные условия, где реконсиляция занимает намного больше обновления ДОМа. Это замедление было сделано специально, чтобы показать разницу между двумя версиями реакта. Мол получает тут сравнимую скорость только из-за того, что эти библиотеки разные, и ненормальные условия по-разному влияют на них. В реальных приложениях такого замедления нет (ваш к.о.), мол не получит такого выигрыша.

                                                                                                  Дмитрий, вы не согласны, что это не бенчмарк?))
                                                                                                  • 0
                                                                                                    Это ненормальные условия, где реконсиляция занимает намного больше обновления ДОМа.

                                                                                                    Не реконциляция, а рендеринг приложения в vDOM. Какой-нибудь сложный фильтр в реальном приложении запросто может занять и куда больше времени.


                                                                                                    Терминологические споры мне вести не интересно.

                                                                                                    • 0
                                                                                                      Да, я именно это и имел ввиду.

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

                                                                                                      И какой же сложный фильтр может быть в функции рендера?))
                                                                                                      • –1

                                                                                                        Товары с популярностью выше среднего в тексте названия, типа и описания которых встречаются подстроки "сини" и "штан".

                                                                                                        • 0
                                                                                                          Какая ужасная у вас функция рендера. Ну да ладно, на вкус и цвет все фломастеры разные. Боюсь в этой ветке уже совсем нечего обсуждать, но вы хотя бы попытались. У вас остались шансы получше использовать демагогию в других ветках, а ведь демагогия — ваш конёк :)
                                                                                                          • 0

                                                                                                            У вас есть в стейте список товаров и список фильтров. Вам надо нарисовать отфильтрованный список товаров. Куда вы засунете фильтрацию?

                                                                                    • 0

                                                                                      1 — размазывание рендеринга по фреймам никак не поможет ctrl+f полноценно заработать раньше окончания всего рендеринга. В худшем случае он может вообще никогда не завершиться, если постоянно будут более приоритетные задачи (скриптовая анимация какая-нибудь в верху страницы).


                                                                                      2 — Реакт размазывает по фреймам лишь свой маленький кусочек работы. Есть ещё большой пласт работ по подготовке данных, по отрисовке оных и прочие задачи, на которые банально может не хватить времени до конца фрейма, большая часть времени которого уже потрачена на рендеринг невидимых частей.


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


                                                                                      4 — reflow (обновление дерева css-блоков), repaint (отрисовка css-блоков), recomposition (формирование итоговой картинки) — это части браузерного пайплайна, которые отрабатывают уже после внесения изменений в DOM.


                                                                                      5 — ничто не мешает по ctrl+f фокусировать встроенный поиск.


                                                                                      Вы попробуйте своё прожорливое приложение на среднестатистической мобилке.

                                                                                      • 0
                                                                                        Первым делом уточню: я нигде не требовал поиск по Ctrl+F. Возможно вы меня путаете с предыдущими собеседниками :) Более того, благодаря переходу на файберс в реакте в ближайшее время появится возможность оптимизировать рендер, отменяя операции для элементов, не попадающих во вьюпорт, и я этого очень жду) Ctrl+F в этом случае тоже ожидаемо не будет работать.

                                                                                        1. См. пост выше про обман себя. У реакт@16 нет такого, что он может не вывести числа. Полный рендер большой портянки гридов завершится не за один присест с фризом, а за несколько кадров без фризов. Для пользователя это будет относительно незаметно, так как большая часть элементов будут уже не видны, а первые (видимые во вьюпорте) появляются сразу же.

                                                                                        2. Но мы же говорили именно про оптимизацию рендера :) Проблемы с тяжелой работой с данными, не относящиеся к реакту, будут в любом случае, это отдельный вопрос, никак не относящийся к нашему обсуждению. В остальном — кооперативная мультизадачность тасков рендера в реакте поддерживает приоритет операций, и для операций от пользователя он наивысший.

                                                                                        3. Нет, моё требование было в том, чтобы пользователь мог тут же проскроллить к нужному месту большую портянку. Она должна быть сформирована как можно раньше, отложенно, но не лениво. Ctrl+F у нас вообще был невозможен, это же canvas, как упомянул выше. В случае мол это является большой проблемой, которую вы игнорируете с завидным упорством. А в ваш бенчмарк "Время вывода значительных объёмов данных в WEB приложениях" надо добавить ещё один шаг: пара десятков прокруток на произвольные места отрендеренного списка. Но этого никогда не будет сделано, так как вы любите обманывать себя.

                                                                                        4. Ваши исходные слова:
                                                                                        Чем больше элементов в ДОМ, тем медленнее происходит reflow, repaint и прочее.
                                                                                        Вы что, думаете реакт по отдельности каждый див вставляет?) А минимизировать изменения ДОМа как раз реакт помогает.

                                                                                        5. Это сводится к тому же самому:
                                                                                        — у вас в демке с вашим мол скролл фризит намертво всю страницу!
                                                                                        — а вы скролл не дёргайте.

                                                                                        В итоге вы предлагаете костыль вместо привычного решения. И не факт, что полнотекстовый поиск вообще реализован. И не всегда его можно будет реализовать) Например на страничке с 22-я агрегированными списками (внутренний инструмент для отчётов по разным срезам). Мы там наоборот специально пришли к браузерному Ctrl+F как к наиболее удобному решению. И да, мы костыль с перехватом Ctrl+F никак не решает основной проблемы: возможности сразу начать скроллить. Молу от этого плохеет настолько, что страница фризится намертво.

                                                                                        То наше приложение из пункта 3 работало на IPad 2. Который старый, а не новый mini.
                                                                                        • 0
                                                                                          Проблемы с тяжелой работой с данными, не относящиеся к реакту, будут в любом случае

                                                                                          Если что-то можно не рендерить, то можно и не подготавливать для него данные и даже можно их не загружать. Ленивый рендеринг имеет системный эффект. Пока вы только ещё готовите данные для передачи их реакту для рендеринга портянки блоков, я уже покажу те, что видимые.


                                                                                          Нет, моё требование было в том, чтобы пользователь мог тут же проскроллить к нужному месту большую портянку.

                                                                                          К нужному месту скроллбаром у него не получится. А вот поиск бы помог.


                                                                                          Но этого никогда не будет сделано, так как вы любите обманывать себя.

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


                                                                                          Вы что, думаете реакт по отдельности каждый див вставляет?)

                                                                                          Вам сюда: https://webo.in/articles/all/2009/31-rending-restyle-reflow-relayout/


                                                                                          В итоге вы предлагаете костыль вместо привычного решения.

                                                                                          Я настаиваю на удобном для пользователя решении. Стандартный поиск весьма не удобен.

                                                                                          • 0
                                                                                            Мы реакт обсуждаем. С чего вы взяли, что мой сервис/сага будут готовить данные для невидимых компонентов?))

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

                                                                                            И какие же это маргинальные кейзы, если каждый второй человек в комментариях вам писал об этом?) Н

                                                                                            Вы прямо светоч знаний. Правда по-прежнему непонятно, зачем вы это в ответ на необходимость отрендерить список. Только не говорите, что вы пугаетесь много рендерить и из-за этого даёте пользователям страдать, когда все первым делом тянутся к скроллу и мол фризит вкладку :)

                                                                                            И опять же, каждый второй пользователь вам говорил, что это неудобно. Но вам виднее :)
                                                                                            • 0
                                                                                              Мы реакт обсуждаем. С чего вы взяли, что мой сервис/сага будут готовить данные для невидимых компонентов?))

                                                                                              С того, что Реакт имеет ведомую архитектуру. Что для него подготовили — то он и отрендерил. $mol, наоборот имеет, ведущую архитектуру — что он запросил, то для него и подготовили. Вам, чтобы инвертировать архитерктуру, нужно будет вручную поплясать вокруг детектирования видимых элементов данных в реальном времени и подготовкой только нужного.


                                                                                              И какие же это маргинальные кейзы, если каждый второй человек в комментариях вам писал об этом?)

                                                                                              Поиск малейшего изъяна и выдача его за огромную дыру — типичная реакция, когда покушаются на святое.

                                                                                              • +1
                                                                                                Вам, чтобы инвертировать архитерктуру, нужно будет вручную поплясать вокруг детектирования видимых элементов данных в реальном времени и подготовкой только нужного.
                                                                                                Простите, но я же сам говорю реакту, что рендерить :) Что же такого уникального в ваших десяти строчках? Только вы правильно умеете плясать вокруг детектирования и расширять точку до кругозора? :) Вы в курсе, что реализаций виртуального скроллинга великое множество?))

                                                                                                Поиск малейшего изъяна и выдача его за огромную дыру — типичная реакция, когда покушаются на святое.
                                                                                                Понятно, вопросов больше нет :D

                                                                                                Судя по всему эта ветка тоже закончена, здесь мы обсудили все пункты. Не утруждайтесь, вопросы выше в этом моём комментарии — риторические.
                                                                                                • 0
                                                                                                  Простите, но я же сам говорю реакту, что рендерить :)

                                                                                                  О том и спич, что вам нужно всё-время самому ему говорить что рендерить. Мне же достаточно сказать "сам разберись какие данные тебе нужны и запроси их у меня". Вы бы не смайлики мне ставили, а попробовали разобраться в описываемой мной парадигме — она значительно упрощает разработку.


                                                                                                  Вы в курсе, что реализаций виртуального скроллинга великое множество?))

                                                                                                  $mol_list — ни разу не виртуальный скроллинг. Но виртуальный скроллинг у нас тоже есть.

                                                                                            • 0
                                                                                              Остановлю ваш спор пожалуй, в React 16 вошли далеко не все фишки Fiber. Что-бы обеспечить обратную совместимость и плавный переход с React 15.x некоторый функционал пока опустили, и об этом прямо написано в статье с информацией об изменениях в 16 версии на сайте React. А вот данный пример с треугольником в Facebook показывали на девелоперской версии react'а, с включённым на полную катушку Fiber'ом.
                                                          • 0
                                                            А каким образом так вышло, что mol-jsx работает вдвое быстрее, чем mol, и требует меньше памяти?

                                                            Таким, что реализация бенчмарка на $mol_dom_jsx была сломана и ничего не рендерила. Починил.


                                                            Я думал, что mol-jsx — что-то вроде обертки над чистым mol, логично, что он должен работать медленнее?

                                                            Нет, к $mol компонентам $mol_dom_jsx отношения не имеет. Это просто рендеринг всего дома через JSX без Реакта, не более.

                                                            • +1
                                                              > Таким, что реализация бенчмарка на $mol_dom_jsx была сломана и ничего не рендерила. Починил.

                                                              И оно сразу стало на уровне нейтив-дома. И ангуляр, и реакт — тоже на том же уровне, и только мол каким-то образом в n раз быстрее. При этом с увеличением количества нод затраты на обновление не растут. Выглядит откровенно странно.
                                                              • 0

                                                                При ленивом рендеринге затраты пропорциональны не объёму данных, а размеру экрана.

                                                                • 0
                                                                  > При ленивом рендеринге затраты пропорциональны не объёму данных, а размеру экрана.

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

                                                                  Ну и ссылку на исходники всех этих тестов было бы неплохо иметь.
                                                                  • 0
                                                                    Допустим, какой-то сложный грид, и там на экране десять тыщ таких вот блоков, они все видны.

                                                                    Видимых блоков будет не больше 1000. Ну вот для примера такой грид — всего 700 дом-элементов на моём фулхд экране.


                                                                    Ну и ссылку на исходники всех этих тестов было бы неплохо иметь.

                                                                    Тут все ссылки.

                                                                    • 0
                                                                      > Видимых блоков будет не больше 1000.

                                                                      С чего бы это?

                                                                      > Ну вот для примера такой грид — всего 700 дом-элементов на моём фулхд экране.

                                                                      Так там по блоку на ячейку, а их легко может быть и десяток, и полсотни. Умножаете 700 на 10 — получаете 7к. Как ведет себя $mol с 7к видимыми элементами?
                                                                      • 0

                                                                        Ячейка с 10 блоками внутри и занимать будет в 10 раз больше места, а значит таких ячеек влезет в 10 раз меньше. Число видимых блоков пропорционально площади экрана.

                                                                        • 0
                                                                          А если эти блоки в ячейке — это слои, один выводится поверх другого?
                                                                          • 0

                                                                            Нафантазировать можно много чего. Давайте ближе к реальности — в каком приложении требуется 10к блоков в видимой области?

                                                                            • 0
                                                                              В голову приходит что-то из разряда ненормального программирования: работа с DOM как с пикселями, графики на DOM-элементах, ASCII-подобные мультики.

                                                                              Вопрос в другом, почему mol на видимой области медленнее vdom, чему там тормозить, если обновления точечные и все-равно все упирается в DOM?
                                                                              • 0
                                                                                1. Не всё упирается в ДОМ. Остальные вычисления тоже имеют свою цену. И обеспечение ленивости в том числе.
                                                                                2. В зависимости от бенчмарка может выдать меньше попугайчиков, а может больше.
                                                                                3. Не тормозит. Цель $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
                                                                                • 0
                                                                                  Вы не думали сделать эту фичу отключаемой? Кому очень нужен Ctrl-F, для бенчмарков и для споров.

                                                                                  Отстали бы те, кто использует аргумент ленивости против mol.

                                                                                  Т.к. я использовал атомы (основу mol) вместе с реактом и делал бенчмарки, то думаю, что mol все-равно бы в среднем чуть обгонял популярные фреймворки за счет легковесности.

                                                                                  Даже если скорость будет такая же, она достигается гораздо более простыми алгоритмами, в 5 раз меньшим объемом кода, кастомизируемостью, которых нет у других, а это уже аргумент.
                                                                                  • –1

                                                                                    Я думаю не стоит усложнять интерфейс и реализацию ради столь маргинальных кейсов. Отключить ленивость-то не сложно (установкой minimal_height=0 для всех блоков или отказом от $mol_list), но код получится не идеоматичным. Всё же бенчмарки — это не просто конкурс "кто из эквивалентных реализаций наберёт больше попугайчиков", а способ оценить отзывчивость приложения в различных архитектурах и различных ситуациях по умолчанию и объём трудозатрат, чтобы эта отзывчивость не деградировала. В реальном приложении же никто в здравом уме не будет отключать ленивость — почему это надо делать для бенчмарков, ещё более отдаляя их от реальности?


                                                                                    Ещё показательный пример — бенчмарк вывода графиков. $mol мало того, что выводит всё через один элемент path, а не кучу circle, так ещё и точки выводит не все, схлопывая в одну те, что располагаются слишком близко. Не честно? На Highcharts тоже можно сделать просеивание? Можно, только делать это придётся вручную и скорее всего уже после того, как пользователи достанут саппорт своими жалобами по поводу тормозов, когда они загружают в систему слишком много данных.


                                                                                    Так что я против того, чтобы уравнивать все реализации по самой глупой из них ради никому не нужной "справедливости". Главное — какой видимый результат для пользователя, а не сколько пустой работы было проделано.

                                                                                    • +1
                                                                                      На мой взгляд, изолированные бенчмарки алгоритмов тоже нужны. Тут дело в нестандартных условиях, которые нельзя предугадать. Например, что такое видимая область, это сколько?

                                                                                      А если стена из мониторов, с очень большим суммарным разрешением?

                                                                                      А если это приложение на каком-нибудь electron или phantomjs, задача которого срендерить много-много страниц pdf? Хотя тут наверное SSR поможет, а он у вас без такой оптимизации.

                                                                                      Как будет масштабироваться mol в сравнении с конкурентами на нестандартные решения? Хоть это сейчас и редкие случаи, но интересный был бы задел на будущее.
                                                                                      • 0

                                                                                        Ну так, если стена из мониторов, то и результаты на тех же бенчмарках будут другие. Взял бенчмарк — протестил в своих условиях — что может быть проще? :-) Пытаться экстраполировать данные полученные на маленьком экране на большой — гиблая затея. Например, даже в том же ToDoMVC при увеличении размеров экрана начинает всё сильнее сказываться repaint, который от фреймворка мало зависит.

                                                                                  • 0
                                                                                    > ToDoMVC, 30 задач, отзумили страницу, чтобы все влезали (загрузка, создание, удаление):

                                                                                    Во-первых, мол некорректно обрабатывает этот бенчмарк (не обновляются todo-счетчики), во-вторых — непонятно, чего вы там назапускали, т.к. вот:
                                                                                    i.imgur.com/vea9fYe.png
                                                                                    • 0
                                                                                      мол некорректно обрабатывает этот бенчмарк (не обновляются todo-счетчики)

                                                                                      Какие ещё счётчики? Если речь про число открытых задач в подвале, то оно обновляется.


                                                                                      непонятно, чего вы там назапускали

                                                                                      Я в ФФ запускал под Убунтой, Хрома у меня сейчас нет под рукой.