Wir sind Fans von Benutzerdefinierte Elemente hier in der Gegend. Ihr Design macht sie besonders anfällig für Lazy Loading, was ein Segen für die Leistung sein kann.
Inspiriert durch eines Kollegen Experimente habe ich mich kürzlich daran gemacht, einen einfachen Autoloader zu schreiben: Immer wenn ein benutzerdefiniertes Element im DOM erscheint, wollen wir die entsprechende Implementierung laden, falls sie noch nicht verfügbar ist. Der Browser kümmert sich dann von da an darum, solche Elemente zu aktualisieren.
Wahrscheinlich brauchen Sie das alles gar nicht; Es gibt normalerweise einen einfacheren Ansatz. Bei bewusster Anwendung können die hier gezeigten Techniken dennoch eine nützliche Ergänzung Ihres Toolsets sein.
Aus Konsistenzgründen möchten wir, dass unser Autoloader auch ein benutzerdefiniertes Element ist – was auch bedeutet, dass wir ihn einfach über HTML konfigurieren können. Aber zuerst lassen Sie uns diese nicht aufgelösten benutzerdefinierten Elemente Schritt für Schritt identifizieren:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Angenommen, wir haben dieses Modul im Voraus geladen (mithilfe von async
ist ideal), können wir a fallen lassen <ce-autoloader>
Element in die <body>
unseres Dokuments. Dadurch wird sofort der Erkennungsprozess für alle untergeordneten Elemente von gestartet <body>
, das nun unser Wurzelelement darstellt. Wir könnten die Erkennung auf einen Teilbaum unseres Dokuments beschränken, indem wir hinzufügen <ce-autoloader>
stattdessen auf das jeweilige Container-Element — möglicherweise haben wir sogar mehrere Instanzen für verschiedene Teilbäume.
Das müssen wir natürlich noch umsetzen discover
Methode (als Teil der AutoLoader
Klasse oben):
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); } }
}
Hier überprüfen wir unser Wurzelelement zusammen mit jedem einzelnen Nachkommen (*
). Wenn es sich um ein benutzerdefiniertes Element handelt – wie durch Bindestriche gekennzeichnet – aber noch nicht aktualisiert wurde, versuchen wir, die entsprechende Definition zu laden. Das Abfragen des DOM auf diese Weise kann teuer sein, daher sollten wir ein wenig vorsichtig sein. Wir können die Belastung des Haupt-Threads verringern, indem wir diese Arbeit verschieben:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
wird noch nicht allgemein unterstützt, aber wir können es verwenden requestAnimationFrame
als rückfall:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Jetzt können wir mit der Implementierung des Fehlenden fortfahren load
Verfahren zum dynamischen Injizieren von a <script>
Element:
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`;
}
Beachten Sie die fest codierte Konvention in elementURL
dem „Vermischten Geschmack“. Seine src
Die URL des Attributs geht davon aus, dass es ein Verzeichnis gibt, in dem sich alle benutzerdefinierten Elementdefinitionen befinden (z <my-widget>
→ /components/my-widget.js
). Wir könnten uns ausgefeiltere Strategien einfallen lassen, aber das reicht für unsere Zwecke aus. Das Relegieren dieser URL in eine separate Methode ermöglicht bei Bedarf eine projektspezifische Unterklassenbildung:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Beachten Sie in jedem Fall, dass wir uns darauf verlassen this.rootDir
. Hier kommt die oben erwähnte Konfigurierbarkeit ins Spiel. Fügen wir einen entsprechenden Getter hinzu:
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;
}
Du denkst vielleicht an observedAttributes
jetzt, aber das macht die Sache nicht wirklich einfacher. Plus Aktualisierung root-dir
at runtime scheint etwas zu sein, das wir nie brauchen werden.
Jetzt können – und müssen – wir unser Elements-Verzeichnis konfigurieren: <ce-autoloader root-dir="/components">
.
Damit kann unser Autoloader seine Arbeit erledigen. Nur funktioniert es nur einmal für Elemente, die bereits vorhanden sind, wenn der Autoloader initialisiert wird. Wir werden wahrscheinlich auch dynamisch hinzugefügte Elemente berücksichtigen wollen. Das ist wo MutationObserver
kommt ins Spiel:
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();
}
Auf diese Weise benachrichtigt uns der Browser, wenn ein neues Element im DOM – oder besser gesagt in unserem jeweiligen Teilbaum – auftaucht, das wir dann verwenden, um den Erkennungsprozess neu zu starten. (Sie könnten argumentieren, dass wir hier benutzerdefinierte Elemente neu erfinden, und Sie hätten Recht.)
Unser Autoloader ist jetzt voll funktionsfähig. Zukünftige Verbesserungen könnten potenzielle Rennbedingungen untersuchen und Optimierungen untersuchen. Aber die Chancen stehen gut, dass dies für die meisten Szenarien gut genug ist. Lassen Sie mich in den Kommentaren wissen, wenn Sie einen anderen Ansatz haben, und wir können unsere Notizen vergleichen!
- SEO-gestützte Content- und PR-Distribution. Holen Sie sich noch heute Verstärkung.
- Platoblockkette. Web3-Metaverse-Intelligenz. Wissen verstärkt. Hier zugreifen.
- Quelle: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Über uns
- oben
- Konto
- berührt das Schneidwerkzeug
- hinzugefügt
- Zusatz
- Alle
- lindern
- erlaubt
- bereits
- und
- Ansatz
- argumentieren
- um
- verfügbar
- Browser
- Kandidaten
- kann keine
- österreichische Unternehmen
- vorsichtig
- Chancen
- aus der Ferne überprüfen
- der
- Klasse
- wie die
- Bemerkungen
- vergleichen
- Bedingungen
- Container
- Convention
- Dazugehörigen
- könnte
- Kurs
- Original
- Design
- anders
- Entdeckung
- Dokument
- Tut nicht
- DOM
- Drop
- dynamisch
- einfacher
- leicht
- Erarbeiten
- Elemente
- genug
- Fehler
- Äther (ETH)
- EV
- Sogar
- Jedes
- Außer
- teuer
- Gescheitert
- Fans
- Vorname
- für
- voll
- funktional
- Zukunft
- gehen
- gut
- ganzer
- hier
- HTML
- HTTPS
- ideal
- identifizieren
- sofort
- implementieren
- Implementierung
- Umsetzung
- in
- beantragen müssen
- untersuchen
- IT
- JavaScript
- Job
- Art
- Wissen
- Länge
- LIMIT
- wenig
- Belastung
- Laden
- aussehen
- Main
- um
- MACHT
- Mittel
- Methode
- könnte
- Kommt demnächst...
- Modulen
- mehr
- vor allem warme
- schlauer bewegen
- Mozilla
- mehrere
- Need
- erforderlich
- Neu
- Knoten
- Teil
- Leistung
- Plato
- Datenintelligenz von Plato
- PlatoData
- Play
- erfahren
- Potenzial
- wahrscheinlich
- Prozessdefinierung
- Zwecke
- Rennen
- kürzlich
- entfernen
- diejenigen
- Rückkehr
- Wurzel
- Szenarien
- Umfang
- scheint
- getrennte
- kompensieren
- sollte
- gezeigt
- Einfacher
- Single
- So
- etwas
- Anfang
- Schritt
- Immer noch
- Strategien
- so
- Unterstützte
- TAG
- nimmt
- Techniken
- Das
- ihr
- Denken
- zu
- was immer dies auch sein sollte.
- Aktualisierung
- Upgrade
- URI
- URL
- us
- -
- gewöhnlich
- welche
- werden wir
- Arbeiten
- Werk
- Schreiben
- Ihr
- Zephyrnet