Vi är fans av Anpassade element här omkring. Deras design gör dem särskilt mottaglig för lat lastning, vilket kan vara en välsignelse för prestanda.
Inspirerad av en kollegas experiment, började jag nyligen skriva en enkel auto-loader: Närhelst ett anpassat element dyker upp i DOM vill vi ladda motsvarande implementering om det inte är tillgängligt ännu. Webbläsaren tar sedan hand om att uppgradera sådana element därifrån och ut.
Chansen är stor att du faktiskt inte behöver allt detta; det finns vanligtvis ett enklare tillvägagångssätt. Använd medvetet, teknikerna som visas här kan fortfarande vara ett användbart tillägg till din verktygsuppsättning.
För konsekvens vill vi att vår auto-loader också ska vara ett anpassat element - vilket också innebär att vi enkelt kan konfigurera det via HTML. Men låt oss först identifiera dessa olösta anpassade element, steg för steg:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Förutsatt att vi har laddat den här modulen i förväg (med async
är idealiskt), kan vi släppa en <ce-autoloader>
element i <body>
av vårt dokument. Det kommer omedelbart att starta upptäcktsprocessen för alla underordnade delar av <body>
, som nu utgör vårt rotelement. Vi kan begränsa upptäckten till ett underträd av vårt dokument genom att lägga till <ce-autoloader>
till respektive behållarelement istället - faktiskt, vi kan till och med ha flera instanser för olika underträd.
Det måste vi naturligtvis fortfarande genomföra discover
metoden (som en del av AutoLoader
klass ovan):
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); } }
}
Här kontrollerar vi vårt rotelement tillsammans med varje enskild ättling (*
). Om det är ett anpassat element – vilket indikeras av avstavningstaggar – men ännu inte har uppgraderats, kommer vi att försöka ladda motsvarande definition. Att fråga DOM på det sättet kan vara dyrt, så vi bör vara lite försiktiga. Vi kan minska belastningen på huvudtråden genom att skjuta upp detta arbete:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
stöds inte universellt ännu, men vi kan använda requestAnimationFrame
som en reserv:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Nu kan vi gå vidare till att implementera det saknade load
metod för att dynamiskt injicera 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`;
}
Observera den hårdkodade konventionen i elementURL
. De src
attributets URL förutsätter att det finns en katalog där alla anpassade elementdefinitioner finns (t.ex <my-widget>
→ /components/my-widget.js
). Vi skulle kunna komma med mer utarbetade strategier, men det här är tillräckligt bra för våra syften. Om du förvisar denna URL till en separat metod kan projektspecifik underklassning göras vid behov:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Oavsett vilket, notera att vi förlitar oss på this.rootDir
. Det är här den tidigare nämnda konfigurerbarheten kommer in. Låt oss lägga till en motsvarande 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 kanske tänker på observedAttributes
nu, men det gör inte saken lättare. Plus uppdatering root-dir
vid körning verkar vara något vi aldrig kommer att behöva.
Nu kan vi – och måste – konfigurera vår elementkatalog: <ce-autoloader root-dir="/components">
.
Med detta kan vår auto-loader göra sitt jobb. Förutom att det bara fungerar en gång, för element som redan finns när auto-loader initieras. Vi kommer förmodligen att ta hänsyn till dynamiskt tillagda element också. Det är där MutationObserver
spelar in:
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å så sätt meddelar webbläsaren oss när ett nytt element dyker upp i DOM - eller snarare vårt respektive underträd - som vi sedan använder för att starta om upptäcktsprocessen. (Du kanske hävdar att vi återuppfinner anpassade element här, och du skulle ha rätt.)
Vår auto-loader är nu fullt fungerande. Framtida förbättringar kan undersöka potentiella tävlingsförhållanden och undersöka optimeringar. Men chansen är stor att detta är tillräckligt bra för de flesta scenarier. Låt mig veta i kommentarerna om du har ett annat tillvägagångssätt så kan vi jämföra anteckningar!
- SEO-drivet innehåll och PR-distribution. Bli förstärkt idag.
- Platoblockchain. Web3 Metaverse Intelligence. Kunskap förstärkt. Tillgång här.
- Källa: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Om oss
- ovan
- Konto
- faktiskt
- lagt till
- Dessutom
- Alla
- lindra
- tillåter
- redan
- och
- tillvägagångssätt
- argumenterar
- runt
- tillgänglig
- webbläsare
- kandidater
- kan inte
- vilken
- noggrann
- chanser
- ta
- barn
- klass
- komma
- kommentarer
- jämföra
- villkor
- Behållare
- Konventionen
- Motsvarande
- kunde
- Naturligtvis
- beställnings
- Designa
- olika
- Upptäckten
- dokumentera
- inte
- DOM
- Drop
- dynamiskt
- lättare
- lätt
- Utveckla
- element
- tillräckligt
- fel
- Eter (ETH)
- EV
- Även
- Varje
- Utom
- dyra
- Misslyckades
- fans
- Förnamn
- från
- fullständigt
- funktionella
- framtida
- kommer
- god
- huvud
- här.
- html
- HTTPS
- idealisk
- identifiera
- blir omedelbart
- genomföra
- genomförande
- genomföra
- in
- istället
- undersöka
- IT
- JavaScript
- Jobb
- Snäll
- Vet
- Längd
- BEGRÄNSA
- liten
- läsa in
- läser in
- se
- Huvudsida
- göra
- GÖR
- betyder
- metod
- kanske
- saknas
- modul
- mer
- mest
- flytta
- Mozilla
- multipel
- Behöver
- behövs
- Nya
- nod
- del
- prestanda
- plato
- Platon Data Intelligence
- PlatonData
- Spela
- plus
- potentiell
- förmodligen
- process
- syfte
- Lopp
- nyligen
- ta bort
- att
- avkastning
- rot
- scenarier
- omfattning
- verkar
- separat
- in
- skall
- visas
- Enkelt
- enda
- So
- något
- starta
- Steg
- Fortfarande
- strategier
- sådana
- Som stöds
- MÄRKA
- tar
- tekniker
- Smakämnen
- deras
- saker
- Tänkande
- till
- sann
- uppdatering
- uppgraderad
- URI
- URL
- us
- användning
- vanligen
- via
- som
- kommer
- Arbete
- fungerar
- skrivning
- Din
- zephyrnet