Ми фанати Спеціальні елементи неподалік. Їх дизайн робить їх особливо піддається відкладеному завантаженню, що може бути перевагою для продуктивності.
Натхненний колеги експериментів, нещодавно я взявся за написання простого автозавантажувача: кожного разу, коли власний елемент з’являється в DOM, ми хочемо завантажити відповідну реалізацію, якщо вона ще недоступна. Потім браузер піклується про оновлення таких елементів з цього моменту.
Швидше за все, все це вам насправді не знадобиться; зазвичай є простіший підхід. Умисно використані методи, показані тут, можуть бути корисним доповненням до вашого набору інструментів.
Для узгодженості ми хочемо, щоб наш автозавантажувач також був власним елементом — це також означає, що ми можемо легко налаштувати його за допомогою HTML. Але спочатку давайте крок за кроком визначимо ці невирішені спеціальні елементи:
class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; this.discover(scope); }
}
customElements.define("ce-autoloader", AutoLoader);
Припустимо, що ми завантажили цей модуль заздалегідь (за допомогою async
є ідеальним), ми можемо відкинути a <ce-autoloader>
елемент у <body>
нашого документа. Це негайно розпочне процес виявлення для всіх дочірніх елементів <body>
, який тепер становить наш кореневий елемент. Ми могли б обмежити відкриття піддеревом нашого документа, додавши <ce-autoloader>
натомість до відповідного елемента контейнера — справді, ми навіть можемо мати кілька екземплярів для різних піддерев.
Звичайно, ми ще маємо це реалізувати discover
метод (у складі AutoLoader
клас вище):
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); } }
}
Тут ми перевіряємо наш кореневий елемент разом із кожним окремим нащадком (*
). Якщо це настроюваний елемент (на що вказують теги з дефісами), але ще не оновлений, ми спробуємо завантажити відповідне визначення. Запит DOM таким чином може бути дорогим, тому нам слід бути трохи обережними. Ми можемо зменшити навантаження на головний потік, відклавши цю роботу:
connectedCallback() { let scope = this.parentNode; requestIdleCallback(() => { this.discover(scope); });
}
requestIdleCallback
ще не підтримується повсюдно, але ми можемо використовувати requestAnimationFrame
як запасний варіант:
let defer = window.requestIdleCallback || requestAnimationFrame; class AutoLoader extends HTMLElement { connectedCallback() { let scope = this.parentNode; defer(() => { this.discover(scope); }); } // ...
}
Тепер можна переходити до реалізації втраченого load
метод динамічного введення a <script>
Елемент:
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`;
}
Зверніть увагу на жорстко закодовану конвенцію в elementURL
, src
URL-адреса атрибута припускає, що існує каталог, де містяться всі визначення користувацьких елементів (наприклад, <my-widget>
→ /components/my-widget.js
). Ми могли б придумати більш детальні стратегії, але цього достатньо для наших цілей. Перенесення цієї URL-адреси в окремий метод дозволяє створювати підкласи для конкретного проекту, коли це необхідно:
class FancyLoader extends AutoLoader { elementURL(tag) { // fancy logic }
}
У будь-якому випадку, зауважте, що ми покладаємося на this.rootDir
. Ось тут і з’являється вищезгадана можливість налаштування. Давайте додамо відповідний геттер:
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;
}
Можливо, ви думаєте про observedAttributes
зараз, але це не полегшує справи. Плюс оновлення root-dir
під час виконання здається чимось, що нам ніколи не знадобиться.
Тепер ми можемо — і повинні — налаштувати наш каталог елементів: <ce-autoloader root-dir="/components">
.
Завдяки цьому наш автозавантажувач може виконувати свою роботу. За винятком того, що він працює лише один раз для елементів, які вже існують під час ініціалізації автозавантажувача. Ймовірно, ми також захочемо врахувати динамічно додані елементи. Ось де MutationObserver
вступає в гру:
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();
}
Таким чином, браузер сповіщає нас щоразу про появу нового елемента в DOM — або, точніше, у відповідному піддереві, — який ми потім використовуємо для повторного запуску процесу виявлення. (Ви можете стверджувати, що ми тут заново винаходимо спеціальні елементи, і ви будете праві.)
Наш автозавантажувач тепер повністю функціональний. Майбутні вдосконалення можуть розглядати потенційні умови перегонів і досліджувати оптимізацію. Але ймовірно, що цього достатньо для більшості сценаріїв. Дайте мені знати в коментарях, якщо у вас інший підхід, і ми зможемо порівняти нотатки!
- Розповсюдження контенту та PR на основі SEO. Отримайте посилення сьогодні.
- Платоблокчейн. Web3 Metaverse Intelligence. Розширені знання. Доступ тут.
- джерело: https://css-tricks.com/an-approach-to-lazy-loading-custom-elements/
- 1
- a
- МЕНЮ
- вище
- рахунки
- насправді
- доданий
- доповнення
- ВСІ
- полегшувати
- дозволяє
- вже
- та
- підхід
- сперечатися
- навколо
- доступний
- браузер
- кандидатів
- не може
- який
- обережний
- шанси
- перевірка
- дитина
- клас
- Приходити
- коментарі
- порівняти
- Умови
- Контейнер
- Конвенція
- Відповідний
- може
- курс
- виготовлений на замовлення
- дизайн
- різний
- відкриття
- документ
- Ні
- DOM
- Падіння
- динамічно
- легше
- легко
- Розробити
- елементи
- досить
- помилка
- Ефір (ETH)
- EV
- Навіть
- Кожен
- Крім
- дорогий
- не вдалося
- вентилятори
- Перший
- від
- повністю
- функціональний
- майбутнє
- буде
- добре
- голова
- тут
- HTML
- HTTPS
- ідеальний
- ідентифікувати
- негайно
- здійснювати
- реалізація
- реалізації
- in
- замість
- дослідити
- IT
- JavaScript
- робота
- Дитина
- Знати
- довжина
- МЕЖА
- трохи
- загрузка
- погрузка
- подивитися
- головний
- зробити
- РОБОТИ
- засоби
- метод
- може бути
- відсутній
- Модулі
- більше
- найбільш
- рухатися
- Mozilla
- множинний
- Необхідність
- необхідний
- Нові
- вузол
- частина
- продуктивність
- plato
- Інформація про дані Платона
- PlatoData
- Play
- плюс
- потенціал
- ймовірно
- процес
- цілей
- Гонки
- нещодавно
- видаляти
- ті
- повертати
- корінь
- сценарії
- сфера
- Здається,
- окремий
- комплект
- Повинен
- показаний
- простий
- один
- So
- що в сім'ї щось
- старт
- Крок
- Як і раніше
- стратегії
- такі
- Підтриманий
- TAG
- приймає
- методи
- Команда
- їх
- речі
- Мислення
- до
- правда
- оновлення
- підвищений
- URI
- URL
- us
- використання
- зазвичай
- через
- який
- волі
- Work
- працює
- лист
- вашу
- зефірнет