在 Web 组件中使用外部 JS 库

Posted

技术标签:

【中文标题】在 Web 组件中使用外部 JS 库【英文标题】:Using external JS libraries in a web component 【发布时间】:2018-11-14 19:38:35 【问题描述】:

我正在使用 Polymer 2 开发一个 Web 组件,并希望使用第三方 javascript 库,该库并非专门为与 Web 组件一起使用而设计的。据我所知,这样做的唯一方法是在我的 Web 组件的 html 文件中包含一个引用库的 <script> 标记。

我可以看到这样做的几个问题,并想知道是否有任何解决方法,以及确实以这种方式包含第三方库是否被认为是不好的做法。

    外部库可能会设置对页面上的其他组件可见的全局变量,从而允许 Web 组件相互破坏,或破坏它们所在的页面。由于封装经常被吹捧为one of the big advantages of using web components,这似乎是个问题。

    外部库可能会执行 DOM 查询或更新,这些查询或更新将无法访问正在使用它们的 Web 组件的 shadow-dom,因此外部库可能根本无法正常工作,或者可能会更新托管页面的 DOM 再次打破封装。

那么,我是否遗漏了什么,或者这是否意味着在 Web 组件中包含外部库是一个非常糟糕的主意?如果是这样,这似乎是这项技术的巨大限制,因为我们无法利用现有的大量 JS 库。

【问题讨论】:

您尝试使用哪个第三方 JavaScript 库? 【参考方案1】:

如果您有一个外部库来执行 document.querySelector 之类的操作,那么您有两个选择。

    选择不将 ShadowDOM 与您的任何组件一起使用。如果说 不是一个选项,或者如果你真的,真的想使用 shadowDOM 那么: 您需要修改第三方库以允许root 要指定的元素,而不是始终使用 document

除了这两个选项之外,您可能无法使用假定document 可以适用于所有内容的第三方库。

我想另一种选择是重新评估第三方库,看看它是否真的值得使用。

在我的团队中,我们不使用不仅仅是可靠逻辑的第三方库。 moment.js 之类的东西只是逻辑,我们可以毫无问题地使用它们。

但是像 jQuery 这样的东西呢?呸!我看不出组件需要这样的东西。

祝你好运!

【讨论】:

我很好奇为什么投反对票。我很想讨论还有哪些其他选择。【参考方案2】:

实际上我昨天不得不处理完全相同的问题,所以时机很好;)在我的情况下,第一页中的视图有两个部分,一个带有单选按钮,并且由于业务需求取决于用户的单选按钮选择一个输入文本使用谷歌地图自动完成将被启用(或保持禁用)

在这种情况下,在没有谷歌地图库的情况下加载页面效率更高,然后在 web 组件完全呈现后动态加载 gmaps 代码,这会导致交互时间下降 50% :) 这就是我结束了。

注意:loadGoogleMaps() 方法和 initCalled 变量声明在类之外,因此在 web 组件之外(我将它们放在 import 语句下)。我还省略了示例中的大部分类代码,因为它与您的问题无关:)

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...

【讨论】:

以上是关于在 Web 组件中使用外部 JS 库的主要内容,如果未能解决你的问题,请参考以下文章

在 angular4 中使用 money.js(外部 js 库)

尝试在 Vanilla JS 中创建 Web 组件库,你会如何开始?

介绍推荐优秀的Vue UI组件库

优秀的Vue UI组件库

重磅:微信小程序发布WeUI.js 官方视觉组件库!

react.js 时钟组件