Comments 24
Браузер не инициирует лишнюю перерисовку страницы если обращения к DOM API происходят в коде синхронно. "Точечные изменения", которые делают за вас библиотеки - именно так и работают. Из этого следует, что взаимодействовать с DOM напрямую - это вовсе не плохая идея, а напротив, путь к более эффективным решениям. Подтверждением этому является множество современных библиотек, отказавшихся от концепции Virtual DOM. В общем, неплохая статья, где-то до середины.
Браузер не инициирует лишнюю перерисовку страницы если обращения к DOM API происходят в коде синхронно.
Дьявол кроется в деталях — действительно на экране ничего не изменится. А вот вызвать reflow (который может быть весьма вычислительно тяжелым, в зависимости от DOM) в синхронном коде — не особо сложно даже в современнейших браузерах (которые сами по себе сильно оптимизируют reflow/repaint, когда могут).
не особо сложно
Как? Приведите пример.
Поменять что-то в элементе и тут же спросить у него, например, offsetHeight. Вроде ещё недавно такое приводило к рефлоу, хотя может быть сейчас оптимизировали и это.
Хороший пример. Показывает, что получение результатов вычисления требует этих самых вычислений, независимо от степени оптимизации. Ну, то есть, если специально запрашивать рефлоу и он произойдет - то это ожидаемое поведение. Я же говорю о ЛИШНИХ перерисовках, которые никак не оптимизируются на уровне синхронизации с Virtual DOM, потому как при этой синхронизации используется тот-же самый синхронный вызов методов DOM API.
Тут есть такой момент - меняем мы один элемент, а потом спрашиваем что-то от другого. При этом часто нам нужно именно прошлое значение свойства, а не пересчитанное с учётом изменения другого элемента. Грубо говоря, в идеале рендер должен происходить так: сначала прочитали из дома всё, что нам нужно, потом обновили дом с учётом прочитанного. Но это не масштабируется. Когда мы разбиваем на компоненты, каждый из них начинает читать и писать вперемешку, что приводит к лишним пересчётам и неожиданным результатам. Тут бы иметь какую-то форму версионирования или откладывания фактических изменений до завершения рендера.VDOM с эти конечно худо-бедно справляется, но цена за это непомерная. IDOM в этом свете выглядит гораздо лучше.
Библиотека https://github.com/wilsonpage/fastdom в принципе позволяет такое сделать и при разбиении на компоненты: все вызовы чтения/изменения DOM в компонентах надо делать через fastdom.measure()
и fastdom.mutate()
, а библиотека сама их сгруппирует.
Я же говорю о ЛИШНИХ перерисовках, которые никак не оптимизируются на уровне синхронизации с Virtual DOM, потому как при этой синхронизации используется тот-же самый синхронный вызов методов DOM API.
Ну так это всё никак не регулируется спецификациями — это на самом деле чисто browser-specific поведение, что, например, тот же хром не будет делать лишних движений, если может обойтись без reflow. А какой-нибудь старый или просто местячковый браузер — вполне может.
Но, разумеется, общего неправильного посыла статьи это не меняет, потому что как ниже правильно заметили, vdom — он про сохранение привычных абстракций, а не про оптимизацию быстродействия. В конце концов, никто за десятилетия не выкатил разгромных бенчмарков, в которых реакт бы рвал всех по производительности. Совсем наоборот, когда ты ангулярщикам рассказываешь про то, что в реакте лишний раз render() страшно дернуть (много возни с vdom и легко получить брутальный performance hit на ровном месте в некоторых не особо экзотичных обстоятельствах), они на тебя смотрят немного с жалостью.
А потом Ангулящикам показываешь, как одно пролетающее в стороне событие вызывает пересчёт всех компонент приложения, и они уже с грустью смотрят на необходимость переводить все компоненты с автоматического трекинга изменений на ручной.
Про сохранение абстракций — хорошая формулировка, согласен (если добавить к ней «в том числе и...»). Но общий посыл не о том, что VDOM позволяет оптимизировать быстродействие, и не про сам VDOM как таковой. А про то, как вообще с изменением подходов и задач развивалось взаимодействие с DOM. Как решения, дававшие плюсы при одном подходе, оказывались минусами при другом, и это приводило к следующей итерации. Как и в любой статье — что-то упрощено, да. Это неизбежно, ведь и весь путь изменений в работе с DOM не был «предначертан заранее», это результат многих итераций, чтобы получить искомое. Текущее состояние тоже не финал, что-то да придет на смену.
Сравнение на примере точечного изменения тоже не совсем корректно, да и сейчас, как совершенно верно замечено, подход меняется.
Мда, столько костылей и библиотек, только для того, чтобы обойти суровую особенность DOM
которой нет
Хотите сказать что Virtual DOM чудесным образом уменьшает это все при добавлении одной ноды в дерево?
Я открывал профилировщик и сравнивал React с одной мало известной библиотекой - результат время на рендеринг без изменений, процессор и память в разы больше утилизируются при использовании Virtual DOM
Хотите сказать что Virtual DOM чудесным образом уменьшает это все при добавлении одной ноды в дерево?
Когда говорят что virtual-dom очень быстрый, то обычно сравнивают какое-нибудь точечное изменение в DOM, с заменой какого-нибудь большого узла целиком. Аргументируют это тем. что за счёт vDom и реконсиляции библиотека знает что поменять, а ваше %frameworkname% нет и патчит всё по хардкору.
И откровенно говоря, в те времена когда появился React это было более-менее правдиво. Много SPA тех времён и правда нещадно экплуатировали innerHTML
, потому что это было просто. Прогнал новые данные по функции-шаблону, получил кусок HTML, заменил старый на новый. По пути потерял весь state вроде позиции скролла, курсор, :focus и пр…
Сейчас это уже совсем не так, и все популярные решения стараются бить по DOM как можно более точечно. Например, если используется observable, то можно сделать автоматически отдельную подписку на нужные значения, от которых зависит какой-нибудь attribute. Ну и разница выходит в BigO, который очень разный. Где-то идёт зависимость от числа задействованных реактивных элементов, а где-то от числа нод на сравнение с прошлым слепком.
Не будет оно каждый раз перерендериваться. Браузеры не совсем дураки разрабатывают.
Попробуйте в Реакте перенести жирный кусок VDOM из одного родителя в другой - он перерендерит всё с нуля вместо 1 операции с DOM.
Вот интересно. А есть решения где п2 как-то иначе сработает? Имею ввиду автоматику, не вручную. Наверное для этого нужна некая глобальная идентификация таких звеньев. $mol?
Ну да, нужны гуиды не привязанные к положению как в $mol.
Virtual DOM был придуман не для оптимизации перерисовки, а для того чтобы натянуть сову на глобус - заменить innerHTML и продолжать генерить страницы как привыкли.
Angular — единственный фреймворк большой тройки, изначально построенный на архитектуре с использованием template, когда все компоненты пишутся с использованием шаблонов
Я бы сказал, что React (с его клонами) - чуть ли не единственный, где не "используются template".
DOM, который построил Chrome. Или не построил? Или не Chrome? Или не DOM?