somos fans de Elementos personalizados por aquí. Su diseño los hace particularmente susceptible a la carga diferida, lo que puede ser una gran ayuda para el rendimiento.
Inspirado por thesuburbansoapbox.com de un colega experimentos, recientemente comencé a escribir un cargador automático simple: cada vez que aparece un elemento personalizado en el DOM, queremos cargar la implementación correspondiente si aún no está disponible. Luego, el navegador se encarga de actualizar dichos elementos a partir de ahí.
Lo más probable es que en realidad no necesites todo esto; generalmente hay un enfoque más simple. Usadas deliberadamente, las técnicas que se muestran aquí pueden ser una adición útil a su conjunto de herramientas.
Para mantener la coherencia, queremos que nuestro cargador automático también sea un elemento personalizado, lo que también significa que podemos configurarlo fácilmente a través de HTML. Pero primero, identifiquemos esos elementos personalizados sin resolver, paso a paso:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Asumiendo que hemos cargado este módulo por adelantado (usando async
es ideal), podemos soltar un <ce-autoloader>
elemento en el <body>
de nuestro documento. Eso iniciará inmediatamente el proceso de descubrimiento para todos los elementos secundarios de <body>
, que ahora constituye nuestro elemento raíz. Podríamos limitar el descubrimiento a un subárbol de nuestro documento agregando <ce-autoloader>
en su lugar, al elemento contenedor respectivo; de hecho, incluso podríamos tener múltiples instancias para diferentes subárboles.
Por supuesto, todavía tenemos que implementar eso. discover
método (como parte del AutoLoader
clase anterior):
discover(scope) { let candidates = [scope, ...scope.querySelectorAll("*")]; for(let el of candidates) { let tag = el.localName; if(tag.includes("-") && !customElements.get(tag)) { this.load(tag); } }
}
Aquí verificamos nuestro elemento raíz junto con cada descendiente (*
). Si es un elemento personalizado, como lo indican las etiquetas con guiones, pero aún no se ha actualizado, intentaremos cargar la definición correspondiente. Consultar el DOM de esa manera puede ser costoso, por lo que debemos tener un poco de cuidado. Podemos aliviar la carga en el hilo principal aplazando este trabajo:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
aún no es universalmente compatible, pero podemos usar requestAnimationFrame
como alternativa:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Ahora podemos pasar a implementar lo que falta load
método para inyectar dinámicamente un <script>
elemento:
load(tag) { let el = document.createElement("script"); let res = new Promise((resolve, reject) => { el.addEventListener("load", ev => { resolve(null); }); el.addEventListener("error", ev => { reject(new Error("failed to locate custom-element definition")); }); }); el.src = this.elementURL(tag); document.head.appendChild(el); return res;
} elementURL(tag) { return `${this.rootDir}/${tag}.js`;
}
Tenga en cuenta la convención codificada en elementURL
. src
la URL del atributo asume que hay un directorio donde residen todas las definiciones de elementos personalizados (p. ej. <my-widget>
→ /components/my-widget.js
). Podríamos idear estrategias más elaboradas, pero esto es lo suficientemente bueno para nuestros propósitos. Relegar esta URL a un método separado permite la subclasificación específica del proyecto cuando sea necesario:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
De cualquier manera, tenga en cuenta que estamos confiando en this.rootDir
. Aquí es donde entra en juego la configurabilidad antes mencionada. Agreguemos un captador correspondiente:
get rootDir() { let uri = this.getAttribute("root-dir"); if(!uri) { throw new Error("cannot auto-load custom elements: missing `root-dir`"); } if(uri.endsWith("/")) { // remove trailing slash return uri.substring(0, uri.length - 1); } return uri;
}
Podrías estar pensando en observedAttributes
ahora, pero eso realmente no hace las cosas más fáciles. Además de actualizar root-dir
en tiempo de ejecución parece algo que nunca vamos a necesitar.
Ahora podemos, y debemos, configurar nuestro directorio de elementos: <ce-autoloader root-dir="/components">
.
Con esto, nuestro cargador automático puede hacer su trabajo. Excepto que solo funciona una vez, para elementos que ya existen cuando se inicializa el cargador automático. Probablemente también querremos tener en cuenta los elementos agregados dinámicamente. Ahí es donde MutationObserver
entra en juego:
connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); let observer = this._observer = new MutationObserver(mutations => { for(let { addedNodes } of mutations) { for(let node of addedNodes) { defer(() => { this.discover(node); }); } } }); observer.observe(scope, { subtree: true, childList: true });
} disconnectedCallback() { this._observer.disconnect();
}
De esta manera, el navegador nos notifica cada vez que aparece un nuevo elemento en el DOM, o más bien, nuestro subárbol respectivo, que luego usamos para reiniciar el proceso de descubrimiento. (Podría argumentar que estamos reinventando elementos personalizados aquí, y estaría en lo cierto).
Nuestro cargador automático ahora es completamente funcional. Las mejoras futuras podrían analizar posibles condiciones de carrera e investigar optimizaciones. Pero es probable que esto sea lo suficientemente bueno para la mayoría de los escenarios. ¡Déjame saber en los comentarios si tienes un enfoque diferente y podemos comparar notas!
- Distribución de relaciones públicas y contenido potenciado por SEO. Consiga amplificado hoy.
- Platoblockchain. Inteligencia del Metaverso Web3. Conocimiento amplificado. Accede Aquí.
- Fuente: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Sobre
- arriba
- Mi Cuenta
- adicional
- adición
- Todos
- aliviar
- permite
- ya haya utilizado
- y
- enfoque
- argumentar
- en torno a
- Hoy Disponibles
- cada navegador
- candidatos
- no puede
- servicios sociales
- cuidadoso
- posibilidades
- comprobar
- sus hijos
- clase
- cómo
- comentarios
- comparar
- condiciones
- Envase
- Convención
- Correspondiente
- podría
- curso
- personalizado
- Diseño
- una experiencia diferente
- descubrimiento
- documento
- No
- DOM
- Soltar
- dinamicamente
- más fácil
- pasan fácilmente
- Elaborar
- elementos
- suficientes
- error
- Éter (ETH)
- EV
- Incluso
- Cada
- Excepto
- costoso
- Fallidos
- aficionados
- Nombre
- en
- completamente
- funcional
- futuras
- va
- candidato
- cabeza
- esta página
- HTML
- HTTPS
- ideal
- Identifique
- inmediatamente
- implementar
- implementación
- implementación
- in
- investigar
- IT
- JavaScript
- Trabajos
- Tipo
- Saber
- Longitud Mínima
- LIMITE LAS
- pequeño
- carga
- carga
- Mira
- Inicio
- para lograr
- HACE
- significa
- Método
- podría
- que falta
- módulo
- más,
- MEJOR DE TU
- movimiento
- Mozilla
- múltiples
- ¿ Necesita ayuda
- Nuevo
- nodo
- parte
- actuación
- Platón
- Inteligencia de datos de Platón
- PlatónDatos
- Jugar
- más
- posible
- probablemente
- fines
- Carrera
- recientemente
- remove
- aquellos
- volvemos
- raíz
- escenarios
- alcance
- parece
- separado
- set
- tienes
- mostrado
- sencillos
- soltero
- So
- algo
- comienzo
- paso
- Sin embargo
- estrategias
- tal
- Soportado
- ETIQUETA
- toma
- técnicas
- El
- su
- cosas
- Ideas
- a
- verdadero
- actualización
- actualizado
- URI
- Enlance
- us
- utilizan el
- generalmente
- vía
- que
- seguirá
- Actividades:
- funciona
- la escritura
- tú
- zephyrnet