web-dev-qa-db-fra.com

Utilisation de bibliothèques JS externes dans un composant Web

Je développe un composant Web à l'aide de Polymer 2, et j'aimerais utiliser une bibliothèque JavaScript tierce, qui n'a pas été spécialement conçue pour être utilisée avec des composants Web. Pour autant que je sache, la seule façon de le faire est d'inclure un <script> balise référençant la bibliothèque, dans le fichier HTML de mon composant web.

Je peux voir quelques problèmes avec cela, et je veux savoir s'il existe des moyens de les contourner, et en effet si l'inclusion de bibliothèques tierces de cette manière est considérée comme une mauvaise pratique.

  1. La bibliothèque externe peut définir des variables globales qui sont visibles par les autres composants de la page, permettant aux composants Web de se casser les uns les autres ou de casser la page sur laquelle ils sont hébergés. Étant donné que l'encapsulation est souvent présentée comme l'un des grands avantages de l'utilisation de composants Web , cela semble être un problème.

  2. La bibliothèque externe peut effectuer des requêtes ou des mises à jour DOM qui ne pourraient pas accéder au shadow-dom du composant Web qui les utilise, de sorte que la bibliothèque externe peut ne pas fonctionner du tout ou mettre à jour le DOM de la page d'hébergement en cassant à nouveau l'encapsulation .

Alors, est-ce que je manque quelque chose ou cela signifie-t-il que l'inclusion de bibliothèques externes dans un composant Web est une très mauvaise idée? Si c'est le cas, cela semble être une énorme limitation de cette technologie, car nous ne pouvons pas profiter du grand nombre de bibliothèques JS préexistantes.

16
codebox

Si vous avez une bibliothèque externe qui fait des choses comme document.querySelector alors vous avez deux choix.

  1. Choisissez de ne pas utiliser ShadowDOM avec aucun de vos composants. Si ce n'est pas une option, ou si vous voulez vraiment, vraiment utiliser shadowDOM alors:
  2. Vous devez modifier la bibliothèque tierce pour permettre de spécifier un élément racine au lieu de toujours utiliser document.

Au-delà de ces deux options, vous ne pourrez probablement pas utiliser une bibliothèque tierce qui suppose que document fonctionnera pour tout.

Je suppose que l'autre option est de réévaluer la bibliothèque tierce et de voir si elle vaut [~ # ~] vraiment [~ # ~] utile .

Dans mon équipe, nous n'utilisons pas de bibliothèques tierces qui ne sont pas simplement une logique solide. Des choses comme moment.js ne sont que de la logique et nous pouvons les utiliser sans problème.

Mais quelque chose comme jQuery? Beurk! Je ne vois pas besoin de quelque chose comme ça pour un composant.

Bonne chance!

6
Intervalia

J'ai dû traiter ce même problème hier, donc un bon timing;) Dans mon cas, la vue de la première page a deux sections, une avec des boutons radio et en raison des besoins de l'entreprise en fonction de la sélection du bouton radio de l'utilisateur, un texte d'entrée avec google maps la saisie semi-automatique serait activée (ou resterait désactivée)

Dans ce scénario, il était beaucoup plus efficace de charger la page sans les bibliothèques google maps, puis de charger dynamiquement le code gmaps après le rendu complet du composant Web, ce qui a entraîné une baisse de 50% du temps en interactif :) Voici ce que j'ai fini par faire .

REMARQUE: La méthode loadGoogleMaps () et la déclaration de variable initCalled sont en dehors de la classe et donc en dehors du composant Web (je les mets sous les instructions d'importation). J'ai également omis la plupart du code de classe de l'exemple car il n'était pas pertinent pour votre question :)

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...
}
3
univ