w3schools uso mutationobserver lugar example eventos está desaprobado changes javascript html html5 mutation-observers

javascript - uso - Rendimiento de MutationObserver para detectar nodos en DOM completo



mutationobserver jquery (1)

Estoy interesado en usar MutationObserver para detectar si un determinado elemento HTML se agrega en algún lugar de una página HTML. Por ejemplo, diré que quiero detectar si se han agregado algunos <li> en cualquier parte del DOM.

Todos los ejemplos de MutationObserver que he visto hasta ahora solo detectan si se agrega un nodo a un contenedor en particular. Por ejemplo:

algo de HTML

<body> ... <ul id=''my-list''></ul> ... </body>

MutationObserver Definición del MutationObserver

var container = document.querySelector(''ul#my-list''); var observer = new MutationObserver(function(mutations){ // Do something here }); observer.observe(container, { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true, characterDataOldValue: true });

Entonces, en este ejemplo, el MutationObserver está configurado para mirar un contenedor muy seguro ( ul#my-list ) para ver si se han agregado algunos <li> .

¿Es un problema si quisiera ser menos específico y estar atento a <li> en todo el cuerpo HTML de esta manera:

var container = document.querySelector(''body'');

Sé que funciona en los ejemplos básicos que configuré para mí ... ¿Pero no se recomienda hacer esto? ¿Esto va a resultar en un bajo rendimiento? Y si es así, ¿cómo detectaría y mediría ese problema de rendimiento?

Pensé que tal vez había una razón por la cual todos los ejemplos de MutationObserver son tan específicos con su contenedor objetivo ... pero no estoy seguro.


Esta respuesta se aplica a páginas grandes y complejas.

Especialmente si se adjunta un observador antes de que la página comience a cargarse (es decir, document_start / document-start en extensiones de Chrome / WebExtensions / userscripts o solo en una secuencia de comandos de página síncrona normal dentro de <head> ), pero también en páginas enormes actualizadas dinámicamente, por ejemplo, comparación de rama en GitHub. Una devolución de llamada no optimizada de MutationObserver puede agregar unos segundos al tiempo de carga de la página si la página es grande y compleja ( 1 , 2 ). La mayoría de los ejemplos y las bibliotecas existentes no tienen en cuenta tales escenarios y ofrecen un código js atractivo, fácil de usar pero lento.

La devolución de llamada de MutationObserver se ejecuta como un microtask que bloquea el procesamiento posterior de DOM y se puede disparar cientos o miles de veces por segundo en una página compleja.

  1. Utilice siempre el generador de perfiles de devtools e intente hacer que la devolución de llamada de su observador consuma menos del 1% del tiempo total de CPU consumido durante la carga de la página.

  2. Evite activar el diseño síncrono forzado accediendo a offsetTop y propiedades similares

  3. Evite usar marcos / bibliotecas DOM complejas como jQuery, prefiera cosas DOM nativas

  4. Al observar los atributos, use la opción attributeFilter: [''attr1'', ''attr2''] en .observe() .

  5. Siempre que sea posible, observe a los padres directos de manera no recursiva ( subtree: false ).
    Por ejemplo, tiene sentido esperar al elemento padre observando el document recursiva, desconectar al observador en caso de éxito, adjuntar uno nuevo no recursivo en este elemento contenedor.

  6. Cuando espere solo un elemento con un atributo id , use getElementById increíblemente rápido en lugar de enumerar la matriz de mutations (puede tener miles de entradas): example .

  7. En caso de que el elemento deseado sea relativamente raro en la página (por ejemplo, iframe u object ), use la colección HTMLC en vivo devuelta por getElementsByTagName y getElementsByClassName y vuelva a verificarlos todos en lugar de enumerar las mutations si tiene más de 100 elementos, por ejemplo.

  8. Evite usar querySelector y especialmente el querySelectorAll extremadamente lento.

  9. Si querySelectorAll es absolutamente inevitable dentro de la devolución de llamada MutationObserver, primero realice una verificación querySelector , y si tiene éxito, continúe con querySelectorAll . En promedio, este combo será mucho más rápido.

  10. Si apunta a navegadores de borde sin sangrado, no use métodos de matriz integrados como forEach, filter, etc. que requieren devoluciones de llamada porque en Chrome V8 estas funciones siempre han sido costosas de invocar en comparación con el clásico for (var i=0 ....) bucle (10-100 veces más lento, pero el equipo V8 está trabajando en ello [2017]), y la devolución de llamada de MutationObserver puede addedNodes 100 veces por segundo con docenas, cientos o miles de addedNodes en cada lote de mutaciones en páginas modernas complejas .

    La alineación de los arrays incorporados no es universal, generalmente ocurre en código primitivo similar a un punto de referencia. En el mundo real, MutationObserver tiene picos de actividad intermitentes (como 1-1000 nodos reportados 100 veces por segundo) y las devoluciones de llamada nunca son tan simples como return x * x por lo que el código no se detecta como "caliente" lo suficiente como para estar en línea / optimizado .

    Sin embargo, la enumeración funcional alternativa respaldada por lodash o una biblioteca rápida similar está bien. A partir de 2018, Chrome y el V8 subyacente incorporarán los métodos integrados de la matriz estándar.

  11. Si apunta a navegadores de borde sin sangrado, no use los bucles lentos de ES2015 como for (let v of something) dentro de la devolución de llamada MutationObserver a menos que transpile para que el código resultante se ejecute tan rápido como el bucle clásico.

  12. Si el objetivo es alterar el aspecto de la página y tiene un método confiable y rápido para decir que los elementos que se agregan están fuera de la parte visible de la página, desconecte el observador y programe una setTimeout(fn, 0) y reprocesamiento de la página completa a través de setTimeout(fn, 0) : se ejecutará cuando finalice la explosión inicial de la actividad de análisis / diseño y el motor pueda "respirar", lo que podría tomar incluso un segundo. Luego, puede procesar discretamente la página en fragmentos utilizando requestAnimationFrame, por ejemplo.

De vuelta a la pregunta:

mire un contenedor muy seguro ul#my-list para ver si se le ha agregado alguna <li> .

Como li es un hijo directo y buscamos nodos añadidos, la única opción necesaria es childList: true (consulte el consejo n.º 2 anterior).

new MutationObserver(function(mutations, observer) { // Do something here // Stop observing if needed: observer.disconnect(); }).observe(document.querySelector(''ul#my-list''), {childList: true});