Использование внешних JS-библиотек в веб-компоненте
Я разрабатываю веб-компонент с использованием Polymer 2 и хотел бы использовать стороннюю библиотеку JavaScript, которая специально не предназначена для использования с веб-компонентами. Насколько я знаю, единственный способ сделать это - включить <script>
ссылающийся на библиотеку, в HTML файл моего веб-компонента.
Я вижу пару проблем с этим, и хочу знать, существуют ли какие-либо способы их решения, и действительно ли это касается сторонних библиотек таким образом, считается плохой практикой.
-
Внешняя библиотека может устанавливать глобальные переменные, которые видны другим компонентам на странице, позволяя веб-компонентам сломать друг друга или сломать страницу, на которой они размещены. Поскольку инкапсуляция часто рекламируется как одно из больших преимуществ использования веб-компонентов, это кажется проблемой.
-
Внешняя библиотека может выполнять запросы или обновления DOM, которые не смогут получить доступ к теневому домену веб-компонента, который их использует, поэтому внешняя библиотека может вообще не работать вообще или может обновить страницу DOM для хостинга, снова нарушающую инкапсуляцию,
Итак, я что-то упускаю, или это означает, что включение внешних библиотек в веб-компонент - это действительно плохая идея? Если это так, это кажется огромным ограничением этой технологии, поскольку мы не можем воспользоваться огромным количеством ранее существовавших JS-библиотек.
Ответы
Ответ 1
Если у вас есть внешняя библиотека, которая делает такие вещи, как document.querySelector
тогда у вас есть два варианта.
- Выберите, чтобы не использовать ShadowDOM с любыми вашими компонентами. Если это не вариант, или если вы действительно, ДЕЙСТВИТЕЛЬНО хотите использовать shadowDOM, тогда:
- Вам необходимо изменить стороннюю библиотеку, чтобы разрешить указание корневого элемента, а не всегда использовать
document
.
Помимо этих двух вариантов вы, вероятно, не сможете использовать стороннюю библиотеку, которая предполагает, что document
будет работать на все.
Я предполагаю, что другой вариант - переоценить стороннюю библиотеку и посмотреть, действительно ли это стоит.
В моей команде мы не используем сторонние библиотеки, которые не просто твердую логику. Такие вещи, как moment.js, являются просто логикой, и мы можем использовать их без проблем.
Но что-то вроде jQuery? Тьфу! Я не вижу необходимости что-то подобное для компонента.
Удачи!
Ответ 2
На самом деле мне приходилось иметь дело с тем же самым вопросом вчера, так хорошее время;) В моем случае представление на первой странице имеет два раздела, один с переключателями и из-за бизнес-требований, в зависимости от выбора кнопки пользователя, входной текст с картами google автозаполнение будет включено (или останется отключенным)
В этом сценарии было гораздо более эффективно загружать страницу без библиотек карт Google, а затем динамически загружать код gmaps после того, как веб-компонент был полностью отображен, что приведет к 50% -ному сокращению времени до интерактивного. Вот что я в итоге сделал,
ПРИМЕЧАНИЕ. Метод loadGoogleMaps() и объявление переменной initCalled находятся за пределами класса и, следовательно, вне веб-компонента (я помещаю их в операторы импорта). Я также пропустил большую часть кода класса из примера, поскольку он не имел отношения к вашему вопросу :)
import { html } from '@polymer/lit-element';
import { PageViewElement } from './page-view-element.js';
import { SharedStyles } from './shared-styles.js';
import '@vaadin/vaadin-radio-button/vaadin-radio-button.js';
import '@vaadin/vaadin-radio-button/vaadin-radio-group.js';
import { spinner } from './my-icons.js';
let initCalled;
function loadGoogleMaps() {
//Only load gmaps if it has not been loaded before (tracked by the initCalled flag)
if (!initCalled) {
//Dynamically import the library and append it to the document header
const script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.onload = function () {
//Code to execute after the library has been downloaded parsed and processed by the browser starts here :)
initCalled = true;
//TODO: Refactor this DOM traversing logic
const searchAutocomplete = document.querySelector('my-app').shadowRoot.querySelector("home-property-view")
.shadowRoot.querySelector('home-property').shadowRoot.querySelector("input[type='text']");
const autocomplete = new google.maps.places.Autocomplete(
searchAutocomplete, {
types: ['address'],
componentRestrictions: { //Limit to only US addresses
'country': 'us'
}
});
autocomplete.addListener('place_changed', function () {
const place = autocomplete.getPlace();
dispatchEvent(new CustomEvent('propertyAddressChanged', {
bubbles: true,
composed: true,
detail: place
}));
});
};
//Specify the location of the gmaps library
script.src = '//maps.googleapis.com/maps/api/js?v=3.33&key=<YOUR-API-KEY-GOES-HERE>&libraries=places';
//Append it to the document header
document.head.appendChild(script);
}
}
class HomeProperty extends PageViewElement {
//....omitted class code for brevity...
_didRender(props, changedProps, prevProps) {
loadGoogleMaps();
}
//....omitted class code for brevity...
}