Ответ 1
Этот ответ относится к большим и сложным страницам.
Особенно, если наблюдатель прикреплен до начала загрузки страницы (то есть document_start
/document-start
в расширениях Chrome/WebExtensions/userscripts или просто на обычной синхронной странице script внутри <head>
), но также и на огромных динамически обновляемые страницы, например отраслевое сравнение на GitHub. Неоптимизированный обратный вызов MutationObserver может добавить несколько секунд к времени загрузки страницы, если страница большая и сложная (1, 2). Большинство примеров и существующих библиотек не учитывают такие сценарии и предлагают красивый, простой в использовании, но медленный код js.
Обратный вызов MutationObserver выполняется как микрозадача, которая блокирует дальнейшую обработку DOM и может быть запущена сотнями или тысячами раз в секунду на сложной странице.
-
Всегда используйте devtools profiler и старайтесь, чтобы ваш обратный вызов наблюдателя потреблял меньше 1% общего времени процессора, потребляемого во время загрузки страницы.
-
Избегайте триггерного принудительного синхронного макета путем доступа к offsetTop и аналогичным свойствам
-
Избегайте использования сложных фреймворков/библиотек DOM, таких как jQuery, предпочитайте собственные материалы DOM
-
При наблюдении атрибутов используйте параметр
attributeFilter: ['attr1', 'attr2']
в.observe()
. -
По возможности наблюдайте прямых родителей нерекурсивно (
subtree: false
).
Например, имеет смысл дождаться родительского элемента путем рекурсивного наблюденияdocument
, отсоедините наблюдателя от успеха, прикрепите новый нерекурсивный элемент к этому элементу контейнера. -
Ожидая только один элемент с атрибутом
id
, используйте безумно быстрыйgetElementById
вместо перечисления массиваmutations
(он может содержать тысячи записей): пример. -
Если нужный элемент относительно редко встречается на странице (например,
iframe
илиobject
), используйте живой HTMLCollection, возвращаемыйgetElementsByTagName
иgetElementsByClassName
, и перепроверьте их все вместо перечисленияmutations
если у него более 100 элементов, например. -
Избегайте использования
querySelector
и особенно чрезвычайно медленногоquerySelectorAll
. -
Если
querySelectorAll
абсолютно неизбежно в обратном вызове MutationObserver, сначала выполните проверкуquerySelector
, и в случае успеха выполнитеquerySelectorAll
. В среднем такая комбо будет намного быстрее. -
Если вы используете таргетинг на небедренные пограничные браузеры, не используйте встроенные методы Array, такие как forEach, filter и т.д., которые требуют обратных вызовов, потому что в Chrome V8 эти функции всегда были дорогими для вызова по сравнению с классическим
for (var i=0 ....)
(в 10-100 раз медленнее, но команда V8 работает над ним [2017]), а обратный вызов MutationObserver может срабатывать 100 раз в секунду с десятками, сотнями или тысячамиaddedNodes
в каждой партии мутаций на сложных современных страницах,Встраивание встроенных массивов не является универсальным, обычно это происходит в базовом коде примитивного кода. В реальном мире MutationObserver имеет прерывистые всплески активности (например, 1-1000 узлов, сообщенных 100 раз в секунду), а обратные вызовы никогда не такие простые, как
return x * x
, поэтому код не распознается как "горячий", чтобы быть встроенным/оптимизированным.Альтернативное функциональное перечисление, поддерживаемое lodash или подобной быстрой библиотекой, в порядке. Начиная с 2018 года Chrome и базовый V8 встроят встроенные методы стандартного массива.
-
Если вы используете таргетинг на небедренные пограничные браузеры, не используйте медленные циклы ES2015, например
for (let v of something)
в обратном вызове MutationObserver, если только вы transpile, чтобы полученный код работал так же быстро, как классический циклfor
. -
Если цель состоит в том, чтобы изменить способ просмотра страницы, и у вас есть надежный и быстрый способ сообщить, что добавляемые элементы находятся за пределами видимой части страницы, отключите наблюдателя и запланируйте повторную проверку всей страницы и повторную обработку через
setTimeout(fn, 0)
: он будет выполнен, когда начальный всплеск активности разбора/макетирования закончен, и двигатель может "дышать", что может занять даже секунду. Затем вы можете незаметно обрабатывать страницу в кусках, используя requestAnimationFrame, например.
Вернуться к вопросу:
наблюдайте за очень определенным контейнером
ul#my-list
, чтобы увидеть, добавлены ли к нему<li>
.
Так как li
является прямым дочерним элементом, и мы ищем добавленные узлы, единственная необходимая опция - childList: true
(см. совет № 2 выше).
new MutationObserver(function(mutations, observer) {
// Do something here
// Stop observing if needed:
observer.disconnect();
}).observe(document.querySelector('ul#my-list'), {childList: true});