Suntem fani ai Elemente personalizate pe aici. Designul lor le face în special predispus la încărcare leneșă, care poate fi un avantaj pentru performanță.
Inspirat de a unui coleg experimente, recent m-am apucat să scriu un simplu auto-încărcare: ori de câte ori apare un element personalizat în DOM, vrem să încărcăm implementarea corespunzătoare dacă nu este încă disponibilă. Browserul se ocupă apoi de actualizarea acestor elemente de acolo încolo.
Sunt șanse să nu veți avea nevoie de toate acestea; de obicei există o abordare mai simplă. Folosite în mod deliberat, tehnicile prezentate aici ar putea fi în continuare un plus util pentru setul dvs. de instrumente.
Pentru consecvență, dorim ca încărcătorul nostru automat să fie și un element personalizat - ceea ce înseamnă, de asemenea, că îl putem configura cu ușurință prin HTML. Dar mai întâi, să identificăm acele elemente personalizate nerezolvate, pas cu pas:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Presupunând că am încărcat acest modul în avans (folosind async
este ideal), putem scăpa a <ce-autoloader>
element în <body>
a documentului nostru. Aceasta va începe imediat procesul de descoperire pentru toate elementele copil ale <body>
, care constituie acum elementul nostru rădăcină. Am putea limita descoperirea la un subarboresc al documentului nostru prin adăugare <ce-autoloader>
în schimb, la elementul container respectiv - într-adevăr, s-ar putea chiar să avem mai multe instanțe pentru diferiți subarbori.
Desigur, mai trebuie să punem în aplicare asta discover
metoda (ca parte a AutoLoader
clasa de mai sus):
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); } }
}
Aici verificăm elementul nostru rădăcină împreună cu fiecare descendent (*
). Dacă este un element personalizat, așa cum este indicat de etichetele cu cratime, dar nu a fost încă actualizat, vom încerca să încărcăm definiția corespunzătoare. Interogarea DOM-ului în acest fel ar putea fi costisitoare, așa că ar trebui să fim puțin atenți. Putem atenua încărcarea firului principal prin amânarea acestei lucrări:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
nu este încă acceptat universal, dar îl putem folosi requestAnimationFrame
ca o rezervă:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Acum putem trece la implementarea celor care lipsesc load
metoda de a injecta dinamic 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`;
}
Rețineți convenția codificată în elementURL
. src
URL-ul atributului presupune că există un director în care se află toate definițiile elementelor personalizate (de ex <my-widget>
→ /components/my-widget.js
). Am putea veni cu strategii mai elaborate, dar acest lucru este suficient de bun pentru scopurile noastre. Relegarea acestei adrese URL la o metodă separată permite subclasarea specifică proiectului atunci când este necesar:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
Oricum, rețineți că ne bazăm pe this.rootDir
. Aici intervine configurabilitatea menționată mai sus. Să adăugăm un getter corespunzător:
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;
}
S-ar putea să te gândești la observedAttributes
acum, dar asta nu ușurează cu adevărat lucrurile. Plus actualizare root-dir
în timpul rulării pare ceva de care nu vom avea niciodată nevoie.
Acum putem – și trebuie – să ne configuram directorul de elemente: <ce-autoloader root-dir="/components">
.
Cu aceasta, încărcătorul nostru automat își poate face treaba. Cu excepția faptului că funcționează o singură dată, pentru elementele care există deja când încărcătorul automat este inițializat. Probabil că vom dori să luăm în considerare și elementele adăugate dinamic. Acolo MutationObserver
intră în joc:
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();
}
În acest fel, browserul ne anunță ori de câte ori apare un nou element în DOM – sau mai degrabă, subarborele nostru respectiv – pe care apoi îl folosim pentru a reporni procesul de descoperire. (Ați putea argumenta că reinventăm elemente personalizate aici și ați avea dreptate.)
Încărcătorul nostru automat este acum complet funcțional. Îmbunătățirile viitoare ar putea analiza condițiile potențiale ale cursei și pot investiga optimizările. Dar sunt șanse ca acest lucru să fie suficient de bun pentru majoritatea scenariilor. Spune-mi în comentarii dacă ai o abordare diferită și putem compara notele!
- Distribuție de conținut bazat pe SEO și PR. Amplifică-te astăzi.
- Platoblockchain. Web3 Metaverse Intelligence. Cunoștințe amplificate. Accesați Aici.
- Sursa: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- Despre Noi
- mai sus
- Cont
- de fapt
- adăugat
- plus
- TOATE
- atenua
- permite
- deja
- și
- abordare
- argumenta
- în jurul
- disponibil
- browser-ul
- candidaţilor
- nu poti
- pasă
- atent
- șansele
- verifica
- copil
- clasă
- cum
- comentarii
- comparaţie
- Condiții
- Recipient
- Convenție
- Corespunzător
- ar putea
- înscrie-te la cursul
- personalizat
- Amenajări
- diferit
- descoperire
- document
- Nu
- HOTĂRÂREA
- Picătură
- dinamic
- mai ușor
- cu ușurință
- Elaborat
- element
- suficient de
- eroare
- Eter (ETH)
- EV
- Chiar
- Fiecare
- Cu excepția
- scump
- A eșuat
- fani
- First
- din
- complet
- funcțional
- viitor
- merge
- bine
- cap
- aici
- HTML
- HTTPS
- ideal
- identifica
- imediat
- punerea în aplicare a
- implementarea
- Punere în aplicare a
- in
- in schimb
- investiga
- IT
- JavaScript
- Loc de munca
- Copil
- Cunoaște
- Lungime
- LIMITĂ
- mic
- încărca
- încărcare
- Uite
- Principal
- face
- FACE
- mijloace
- metodă
- ar putea
- dispărut
- modul
- mai mult
- cele mai multe
- muta
- Mozilla
- multiplu
- Nevoie
- necesar
- Nou
- nod
- parte
- performanță
- Plato
- Informații despre date Platon
- PlatoData
- Joaca
- la care se adauga
- potenţial
- probabil
- proces
- scopuri
- Rasă
- recent
- scoate
- respectiv
- reveni
- rădăcină
- scenarii
- domeniu
- pare
- distinct
- set
- să
- indicat
- simplu
- singur
- So
- ceva
- Începe
- Pas
- Încă
- strategii
- astfel de
- Suportat
- TAG
- ia
- tehnici de
- lor
- lucruri
- Gândire
- la
- adevărat
- actualizarea
- modernizate
- URI
- URL-ul
- us
- utilizare
- obișnuit
- de
- care
- voi
- Apartamente
- fabrică
- scris
- Ta
- zephyrnet