We zijn fan van Aangepaste elementen hier in de buurt. Hun ontwerp maakt ze bijzonder vatbaar voor lui laden, wat een zegen kan zijn voor de prestaties.
Geรฏnspireerd door van een collega experimenten, ben ik onlangs begonnen met het schrijven van een eenvoudige auto-loader: telkens wanneer een aangepast element in de DOM verschijnt, willen we de bijbehorende implementatie laden als deze nog niet beschikbaar is. De browser zorgt vervolgens voor het upgraden van dergelijke elementen vanaf dat moment.
De kans is groot dat je dit allemaal niet echt nodig hebt; er is meestal een eenvoudigere aanpak. Met opzet gebruikt, kunnen de hier getoonde technieken nog steeds een nuttige aanvulling zijn op uw toolset.
Voor consistentie willen we dat onze auto-loader ook een aangepast element is, wat ook betekent dat we het eenvoudig via HTML kunnen configureren. Maar laten we eerst die onopgeloste aangepaste elementen stap voor stap identificeren:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Ervan uitgaande dat we deze module vooraf hebben geladen (met behulp van async
is ideaal), kunnen we een <ce-autoloader>
element in de <body>
van ons document. Dat start onmiddellijk het ontdekkingsproces voor alle onderliggende elementen van <body>
, dat nu ons basiselement vormt. We kunnen de ontdekking beperken tot een substructuur van ons document door toe te voegen <ce-autoloader>
in plaats daarvan naar het respectieve containerelement - inderdaad, we kunnen zelfs meerdere instanties hebben voor verschillende substructuren.
Die moeten we natuurlijk nog implementeren discover
methode (als onderdeel van de AutoLoader
klasse hierboven):
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 controleren we ons root-element samen met elke afstammeling (*
). Als het een aangepast element is, zoals aangegeven door afgebroken tags, maar nog niet is geรผpgraded, proberen we de bijbehorende definitie te laden. Op die manier de DOM bevragen kan duur zijn, dus we moeten een beetje voorzichtig zijn. We kunnen de belasting van de rode draad verlichten door dit werk uit te stellen:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
wordt nog niet universeel ondersteund, maar we kunnen het gebruiken requestAnimationFrame
als terugval:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Nu kunnen we verder gaan met het implementeren van de ontbrekende load
methode om dynamisch een <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`;
}
Let op de hardgecodeerde conventie in elementURL
. De src
De URL van het attribuut gaat ervan uit dat er een directory is waar alle aangepaste elementdefinities zich bevinden (bijv <my-widget>
โ /components/my-widget.js
). We zouden meer uitgebreide strategieรซn kunnen bedenken, maar dit is goed genoeg voor onze doeleinden. Door deze URL naar een afzonderlijke methode te delegeren, is indien nodig projectspecifieke subklasse mogelijk:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Hoe dan ook, merk op dat we vertrouwen op this.rootDir
. Hier komt de bovengenoemde configureerbaarheid om de hoek kijken. Laten we een overeenkomstige getter toevoegen:
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;
}
Je denkt misschien aan observedAttributes
nu, maar dat maakt het er niet echt makkelijker op. Bovendien bijwerken root-dir
tijdens runtime lijkt iets dat we nooit nodig zullen hebben.
Nu kunnen en moeten we onze elementenmap configureren: <ce-autoloader root-dir="/components">
.
Hiermee kan onze auto-loader zijn werk doen. Behalve dat het maar รฉรฉn keer werkt, voor elementen die al bestaan โโwanneer de auto-loader wordt geรฏnitialiseerd. We zullen waarschijnlijk ook rekening willen houden met dynamisch toegevoegde elementen. Dat is waar MutationObserver
komt in het spel:
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();
}
Op deze manier stelt de browser ons op de hoogte wanneer er een nieuw element verschijnt in de DOM - of beter gezegd, onze respectieve substructuur - die we vervolgens gebruiken om het ontdekkingsproces opnieuw te starten. (Je zou kunnen zeggen dat we hier aangepaste elementen opnieuw uitvinden, en je zou een beetje gelijk hebben.)
Onze auto-loader is nu volledig functioneel. Toekomstige verbeteringen kunnen mogelijke race-omstandigheden onderzoeken en optimalisaties onderzoeken. Maar de kans is groot dat dit goed genoeg is voor de meeste scenario's. Laat het me weten in de comments als je een andere aanpak hebt en we kunnen notities vergelijken!
- Door SEO aangedreven content en PR-distributie. Word vandaag nog versterkt.
- Platoblockchain. Web3 Metaverse Intelligentie. Kennis versterkt. Toegang hier.
- Bron: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Over
- boven
- Account
- werkelijk
- toegevoegd
- toevoeging
- Alles
- verlichten
- toestaat
- al
- en
- nadering
- argumenteren
- rond
- Beschikbaar
- browser
- kandidaten
- kan niet
- verzorging
- voorzichtig
- kansen
- controle
- kind
- klasse
- hoe
- opmerkingen
- vergelijken
- voorwaarden
- Containers
- Conventie
- Overeenkomend
- kon
- cursus
- gewoonte
- Design
- anders
- ontdekking
- document
- Nee
- ARREST
- Val
- dynamisch
- gemakkelijker
- gemakkelijk
- uitwerken
- geeft je de mogelijkheid
- genoeg
- fout
- Ether (ETH)
- EV
- Zelfs
- Alle
- Behalve
- duur
- Mislukt
- <p></p>
- Voornaam*
- oppompen van
- geheel
- functioneel
- toekomst
- gaan
- goed
- hoofd
- hier
- HTML
- HTTPS
- ideaal
- identificeren
- per direct
- uitvoeren
- uitvoering
- uitvoering
- in
- verkrijgen in plaats daarvan
- onderzoeken
- IT
- JavaScript
- Jobomschrijving:
- Soort
- blijven
- Lengte
- LIMIT
- Elke kleine stap levert grote resultaten op!
- laden
- het laden
- Kijk
- Hoofd
- maken
- MERKEN
- middel
- methode
- macht
- vermist
- module
- meer
- meest
- beweging
- mozilla
- meervoudig
- Noodzaak
- nodig
- New
- knooppunt
- deel
- prestatie
- Plato
- Plato gegevensintelligentie
- PlatoData
- Spelen
- plus
- potentieel
- waarschijnlijk
- doeleinden
- Race
- onlangs
- verwijderen
- degenen
- terugkeer
- wortel
- scenario's
- omvang
- lijkt
- apart
- reeks
- moet
- getoond
- Eenvoudig
- single
- So
- iets
- begin
- Stap voor
- Still
- strategieรซn
- dergelijk
- ondersteunde
- TAG
- neemt
- technieken
- De
- hun
- spullen
- het denken
- naar
- waar
- bijwerken
- opgewaardeerd
- URI
- URL
- us
- .
- doorgaans
- via
- welke
- wil
- Mijn werk
- Bedrijven
- het schrijven van
- Your
- zephyrnet