Cet article explore une méthode de chargement paresseuse pour les éléments personnalisés afin d'améliorer les performances de la page Web. Cette méthode a été inspirée par les expériences des collègues, et son idée principale était de charger automatiquement le code d'implémentation correspondant après que des éléments personnalisés aient été ajoutés au DOM.
Habituellement, il n'y a pas besoin d'un mécanisme de chargement paresseux aussi complexe, mais les techniques décrites dans cet article sont encore précieuses pour des scénarios spécifiques.
Pour maintenir la cohérence, le chargeur paresseux lui-même est également conçu comme un élément personnalisé pour une configuration facile via HTML. Premièrement, nous devons identifier progressivement les éléments personnalisés non résolus:
class AutoLoader extends HTMLElement { connectedCallback() { const scope = this.parentNode; this.discover(scope); } } customElements.define("ce-autoloader", AutoLoader);
En supposant que nous avons préchargé ce module (idéalement en utilisant la méthode asynchrone), nous pouvons ajouter l'élément <ce-autoloader></ce-autoloader>
au document. Cela commencera immédiatement le processus de recherche pour tous les éléments enfants qui forment l'élément racine. En ajoutant <ce-autoloader></ce-autoloader>
à l'élément de conteneur correspondant, vous pouvez limiter la portée de la recherche au sous-arbre du document, et même utiliser plusieurs instances dans différents sous-arbres.
Ensuite, nous devons implémenter la méthode discover
(dans le cadre de la classe ci-dessus): AutoLoader
discover(scope) { const candidates = [scope, ...scope.querySelectorAll("*")]; for (const el of candidates) { const tag = el.localName; if (tag.includes("-") && !customElements.get(tag)) { this.load(tag); } } }
connectedCallback() { const scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); }); }
Tous les navigateurs ne le prennent pas en charge, vous pouvez utiliser requestIdleCallback
comme plan de sauvegarde: requestAnimationFrame
const defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { const scope = this.parentNode; defer(() => { this.discover(scope); }); } // ... }
, injecter dynamiquement les éléments load
: <script></script>
load(tag) { const el = document.createElement("script"); const res = new Promise((resolve, reject) => { el.addEventListener("load", () => resolve(null)); el.addEventListener("error", () => reject(new Error("未能找到自定义元素定义"))); }); el.src = this.elementURL(tag); document.head.appendChild(el); return res; } elementURL(tag) { return `${this.rootDir}/${tag}.js`; }
. L'URL de l'attribut elementURL
suppose qu'il existe un répertoire contenant toutes les définitions des éléments personnalisés (par exemple src
→ <my-widget></my-widget>
). Nous pouvons adopter des stratégies plus complexes, mais cela suffit à nos fins. Déléguer cette URL à une méthode distincte pour permettre la sous-classe spécifique au projet si nécessaire: /components/my-widget.js
class FancyLoader extends AutoLoader { elementURL(tag) { // 自定义逻辑 } }
. Il s'agit de la configurabilité mentionnée précédemment. Ajoutons un getter correspondant: this.rootDir
get rootDir() { const uri = this.getAttribute("root-dir"); if (!uri) { throw new Error("无法自动加载自定义元素:缺少`root-dir`属性"); } return uri.endsWith("/") ? uri.substring(0, uri.length - 1) : uri; }
parce que la mise à jour de la propriété observedAttributes
ne semble pas nécessaire au moment de l'exécution. root-dir
. <ce-autoloader root-dir="/components"></ce-autoloader>
entre en jeu: MutationObserver
class AutoLoader extends HTMLElement { connectedCallback() { const scope = this.parentNode; this.discover(scope); } } customElements.define("ce-autoloader", AutoLoader);
De cette manière, le navigateur nous informera lorsqu'un nouvel élément apparaît dans le DOM (plus précisément, notre sous-arbre correspondant), puis nous l'utilisons pour redémarrer le processus de recherche.
Notre Autoloader est désormais pleinement utilisable. Les améliorations futures peuvent inclure l'étude des conditions de compétition potentielles et l'optimisation. Mais pour la plupart des scénarios, cela suffit. Si vous avez une approche différente, faites-le moi savoir dans les commentaires et nous pouvons communiquer entre nous!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!