Vi er fans av Egendefinerte elementer rundt her. Designet deres gjør dem spesielt mottagelig for lat lasting, som kan være en velsignelse for ytelsen.
Inspirert av en kollegas eksperimenter, har jeg nylig begynt å skrive en enkel auto-loader: Når et tilpasset element vises i DOM, vil vi laste inn den tilsvarende implementeringen hvis den ikke er tilgjengelig ennå. Nettleseren sørger da for å oppgradere slike elementer derfra og ut.
Sjansen er stor for at du faktisk ikke trenger alt dette; det er vanligvis en enklere tilnærming. Brukt bevisst, kan teknikkene vist her fortsatt være et nyttig tillegg til verktøysettet ditt.
For konsistens vil vi at auto-loaderen vår også skal være et tilpasset element - noe som også betyr at vi enkelt kan konfigurere den via HTML. Men først, la oss identifisere de uløste tilpassede elementene, trinn for trinn:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Forutsatt at vi har lastet inn denne modulen på forhånd (ved hjelp av async
er ideell), kan vi slippe en <ce-autoloader>
element inn i <body>
av dokumentet vårt. Det vil umiddelbart starte oppdagelsesprosessen for alle underordnede elementer av <body>
, som nå utgjør vårt rotelement. Vi kan begrense oppdagelsen til et undertre av dokumentet vårt ved å legge til <ce-autoloader>
til det respektive beholderelementet i stedet - ja, vi kan til og med ha flere forekomster for forskjellige undertrær.
Det må vi selvsagt fortsatt gjennomføre discover
metode (som en del av 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 sjekker vi rotelementet vårt sammen med hver eneste etterkommer (*
). Hvis det er et tilpasset element – som indikert med bindestrek-tagger – men ikke oppgradert ennå, prøver vi å laste inn den tilsvarende definisjonen. Å spørre DOM på den måten kan være dyrt, så vi bør være litt forsiktige. Vi kan redusere belastningen på hovedtråden ved å utsette dette arbeidet:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
er ikke universelt støttet ennå, men vi kan bruke requestAnimationFrame
som en reserve:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Nå kan vi gå videre til å implementere det savnede load
metode for å dynamisk injisere 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`;
}
Legg merke til den hardkodede konvensjonen i elementURL
. De src
attributts URL antar at det er en katalog der alle egendefinerte elementdefinisjoner ligger (f.eks <my-widget>
→ /components/my-widget.js
). Vi kunne kommet med mer forseggjorte strategier, men dette er godt nok for våre formål. Relegering av denne nettadressen til en egen metode gjør det mulig for prosjektspesifikk underklassifisering ved behov:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Uansett, merk at vi er avhengige av this.rootDir
. Det er her den nevnte konfigurerbarheten kommer inn. La oss legge til 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 tenker kanskje på observedAttributes
nå, men det gjør egentlig ikke ting enklere. Pluss oppdatering root-dir
under runtime virker som noe vi aldri kommer til å trenge.
Nå kan og må vi konfigurere elementkatalogen vår: <ce-autoloader root-dir="/components">
.
Med dette kan vår auto-loader gjøre jobben sin. Bortsett fra at det bare fungerer én gang, for elementer som allerede eksisterer når auto-loader er initialisert. Vi vil sannsynligvis også ta hensyn til dynamisk lagt til elementer. Det er hvor MutationObserver
spiller inn:
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åten varsler nettleseren oss når et nytt element vises i DOM - eller rettere sagt, vårt respektive undertre - som vi deretter bruker til å starte oppdagelsesprosessen på nytt. (Du kan hevde at vi gjenoppfinner tilpassede elementer her, og du vil ha rett.)
Vår auto-loader er nå fullt funksjonell. Fremtidige forbedringer kan se på potensielle løpsforhold og undersøke optimaliseringer. Men sjansen er stor for at dette er godt nok for de fleste scenarier. Gi meg beskjed i kommentarene hvis du har en annen tilnærming, så kan vi sammenligne notater!
- SEO-drevet innhold og PR-distribusjon. Bli forsterket i dag.
- Platoblokkkjede. Web3 Metaverse Intelligence. Kunnskap forsterket. Tilgang her.
- kilde: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Om oss
- ovenfor
- Logg inn
- faktisk
- la til
- tillegg
- Alle
- lindre
- tillater
- allerede
- og
- tilnærming
- argumentere
- rundt
- tilgjengelig
- nett~~POS=TRUNC leseren~~POS=HEADCOMP
- kandidater
- kan ikke
- hvilken
- forsiktig
- sjansene
- sjekk
- barn
- klasse
- Kom
- kommentarer
- sammenligne
- forhold
- Container
- Konvensjonen
- Tilsvarende
- kunne
- kurs
- skikk
- utforming
- forskjellig
- Funnet
- dokument
- ikke
- DOM
- Drop
- dynamisk
- enklere
- lett
- Utdype
- elementer
- nok
- feil
- Eter (ETH)
- EV
- Selv
- Hver
- Unntatt
- dyrt
- Mislyktes
- fans
- Først
- fra
- fullt
- funksjonelle
- framtid
- skal
- god
- hode
- her.
- HTML
- HTTPS
- ideell
- identifisere
- umiddelbart
- iverksette
- gjennomføring
- implementere
- in
- i stedet
- undersøke
- IT
- Javascript
- Jobb
- Type
- Vet
- Lengde
- BEGRENSE
- lite
- laste
- lasting
- Se
- Hoved
- gjøre
- GJØR AT
- midler
- metode
- kunne
- mangler
- moduler
- mer
- mest
- flytte
- Mozilla
- flere
- Trenger
- nødvendig
- Ny
- node
- del
- ytelse
- plato
- Platon Data Intelligence
- PlatonData
- Spille
- i tillegg til
- potensiell
- sannsynligvis
- prosess
- formål
- Race
- nylig
- fjerne
- de
- retur
- root
- scenarier
- omfang
- synes
- separat
- sett
- bør
- vist
- Enkelt
- enkelt
- So
- noe
- Begynn
- Trinn
- Still
- strategier
- slik
- Støttes
- TAG
- tar
- teknikker
- De
- deres
- ting
- tenker
- til
- sant
- oppdatering
- oppgradert
- URI
- URL
- us
- bruke
- vanligvis
- av
- hvilken
- vil
- Arbeid
- virker
- skriving
- Din
- zephyrnet