Vi er fans af Brugerdefinerede elementer omkring her. Deres design gør dem særlig modtagelig for doven læsning, hvilket kan være en velsignelse for ydeevnen.
Inspireret af en kollegas eksperimenter, gik jeg for nylig i gang med at skrive en simpel auto-loader: Når et brugerdefineret element vises i DOM, vil vi indlæse den tilsvarende implementering, hvis den ikke er tilgængelig endnu. Browseren sørger så for at opgradere sådanne elementer derfra og ud.
Chancerne er, at du faktisk ikke får brug for alt dette; der er normalt en enklere tilgang. Brugt bevidst, kan de viste teknikker stadig være en nyttig tilføjelse til dit værktøjssæt.
For at sikre ensartethed ønsker vi, at vores auto-loader også skal være et brugerdefineret element - hvilket også betyder, at vi nemt kan konfigurere det via HTML. Men lad os først identificere disse uløste brugerdefinerede elementer trin for trin:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Forudsat at vi har indlæst dette modul på forhånd (ved hjælp af async
er ideel), kan vi droppe en <ce-autoloader>
element ind i <body>
af vores dokument. Det vil straks starte opdagelsesprocessen for alle underordnede elementer af <body>
, som nu udgør vores rodelement. Vi kunne begrænse opdagelsen til et undertræ af vores dokument ved at tilføje <ce-autoloader>
til det respektive containerelement i stedet - ja, vi kan endda have flere forekomster for forskellige undertræer.
Det skal vi selvfølgelig stadig implementere discover
metode (som en del af AutoLoader
klasse ovenfor):
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); } }
}
Her tjekker vi vores rodelement sammen med hver enkelt efterkommer (*
). Hvis det er et brugerdefineret element - som angivet med bindestreger - men endnu ikke opgraderet, vil vi forsøge at indlæse den tilsvarende definition. Det kan være dyrt at forespørge DOM på den måde, så vi skal være lidt forsigtige. Vi kan lette belastningen på hovedtråden ved at udsætte dette arbejde:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
er ikke universelt understøttet endnu, men vi kan bruge requestAnimationFrame
som et fald:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Nu kan vi gå videre til at implementere det manglende load
metode til dynamisk at injicere en <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`;
}
Bemærk den hårdtkodede konvention i elementURL
. Det src
attributtens URL antager, at der er en mappe, hvor alle brugerdefinerede elementdefinitioner findes (f.eks <my-widget>
→ /components/my-widget.js
). Vi kunne komme med mere omfattende strategier, men det er godt nok til vores formål. Relegering af denne URL til en separat metode giver mulighed for projektspecifik underklassificering, når det er nødvendigt:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Uanset hvad, bemærk, at vi er afhængige af this.rootDir
. Det er her, den førnævnte konfigurerbarhed kommer ind. Lad os tilføje en tilsvarende getter:
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 tænker måske på observedAttributes
nu, men det gør ikke rigtig tingene nemmere. Plus opdatering root-dir
under runtime virker som noget, vi aldrig får brug for.
Nu kan og skal vi konfigurere vores elementkatalog: <ce-autoloader root-dir="/components">
.
Med dette kan vores auto-loader gøre sit arbejde. Bortset fra, at det kun virker én gang for elementer, der allerede eksisterer, når auto-loaderen initialiseres. Vi vil sandsynligvis også tage højde for dynamisk tilføjede elementer. Det er der MutationObserver
kommer i spil:
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();
}
På denne måde giver browseren os besked, hver gang et nyt element dukker op i DOM - eller rettere sagt vores respektive undertræ - som vi derefter bruger til at genstarte opdagelsesprocessen. (Du kan hævde, at vi genopfinder brugerdefinerede elementer her, og du ville have ret.)
Vores auto-loader er nu fuldt funktionsdygtig. Fremtidige forbedringer kan undersøge potentielle løbsforhold og undersøge optimeringer. Men chancerne er, at dette er godt nok til de fleste scenarier. Fortæl mig i kommentarerne, hvis du har en anden tilgang, og vi kan sammenligne noter!
- SEO Powered Content & PR Distribution. Bliv forstærket i dag.
- Platoblokkæde. Web3 Metaverse Intelligence. Viden forstærket. Adgang her.
- Kilde: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Om
- over
- Konto
- faktisk
- tilføjet
- Desuden
- Alle
- lindre
- tillader
- allerede
- ,
- tilgang
- argumentere
- omkring
- til rådighed
- browser
- kandidater
- kan ikke
- hvilken
- forsigtig
- odds
- kontrollere
- barn
- klasse
- Kom
- kommentarer
- sammenligne
- betingelser
- Container
- Konventionen
- Tilsvarende
- kunne
- kursus
- skik
- Design
- forskellige
- opdagelse
- dokumentet
- Er ikke
- DOM
- Drop
- dynamisk
- lettere
- nemt
- Uddybe
- elementer
- nok
- fejl
- Ether (ETH)
- EV
- Endog
- Hver
- Undtagen
- dyrt
- mislykkedes
- fans
- Fornavn
- fra
- fuldt ud
- funktionel
- fremtiden
- gå
- godt
- hoved
- link.
- HTML
- HTTPS
- ideal
- identificere
- straks
- gennemføre
- implementering
- gennemføre
- in
- i stedet
- undersøge
- IT
- JavaScript
- Job
- Venlig
- Kend
- Længde
- GRÆNSE
- lidt
- belastning
- lastning
- Se
- Main
- lave
- maerker
- midler
- metode
- måske
- mangler
- modul
- mere
- mest
- bevæge sig
- Mozilla
- flere
- Behov
- behov
- Ny
- node
- del
- ydeevne
- plato
- Platon Data Intelligence
- PlatoData
- Leg
- plus
- potentiale
- sandsynligvis
- behandle
- formål
- Løb
- for nylig
- Fjern
- dem
- afkast
- rod
- scenarier
- rækkevidde
- synes
- adskille
- sæt
- bør
- vist
- Simpelt
- enkelt
- So
- noget
- starte
- Trin
- Stadig
- strategier
- sådan
- Understøttet
- TAG
- tager
- teknikker
- deres
- ting
- Tænker
- til
- sand
- opdatering
- opgraderet
- URI
- URL
- us
- brug
- sædvanligvis
- via
- som
- vilje
- Arbejde
- virker
- skrivning
- Din
- zephyrnet