Использование внешних JS-библиотек в веб-компоненте

Я разрабатываю веб-компонент с использованием Polymer 2 и хотел бы использовать стороннюю библиотеку JavaScript, которая специально не предназначена для использования с веб-компонентами. Насколько я знаю, единственный способ сделать это - включить <script> ссылающийся на библиотеку, в HTML файл моего веб-компонента.

Я вижу пару проблем с этим, и хочу знать, существуют ли какие-либо способы их решения, и действительно ли это касается сторонних библиотек таким образом, считается плохой практикой.

  1. Внешняя библиотека может устанавливать глобальные переменные, которые видны другим компонентам на странице, позволяя веб-компонентам сломать друг друга или сломать страницу, на которой они размещены. Поскольку инкапсуляция часто рекламируется как одно из больших преимуществ использования веб-компонентов, это кажется проблемой.

  2. Внешняя библиотека может выполнять запросы или обновления DOM, которые не смогут получить доступ к теневому домену веб-компонента, который их использует, поэтому внешняя библиотека может вообще не работать вообще или может обновить страницу DOM для хостинга, снова нарушающую инкапсуляцию,

Итак, я что-то упускаю, или это означает, что включение внешних библиотек в веб-компонент - это действительно плохая идея? Если это так, это кажется огромным ограничением этой технологии, поскольку мы не можем воспользоваться огромным количеством ранее существовавших JS-библиотек.

Ответ 1

Если у вас есть внешняя библиотека, которая делает такие вещи, как document.querySelector тогда у вас есть два варианта.

  1. Выберите, чтобы не использовать ShadowDOM с любыми вашими компонентами. Если это не вариант, или если вы действительно, ДЕЙСТВИТЕЛЬНО хотите использовать shadowDOM, тогда:
  2. Вам необходимо изменить стороннюю библиотеку, чтобы разрешить указание корневого элемента, а не всегда использовать 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...
}