2

我正在尝试使用 lit-html 创建一个使用 Google Maps API 的自定义元素。怎么可能让它一起工作?这是我到目前为止所拥有的:

地图元素.css:

#map {
    height: 400px;  /* The height is 400 pixels */
    width: 100%;  /* The width is the width of the web page */
    background-color: grey;
}

地图元素.js:

import {
    LitElement,
    html
} from '@polymer/lit-element';

class MapElement extends LitElement {
    render() {
        return html `

        <link rel="stylesheet" href="map-element.css">

        <div id="map"></div>

        <script>
            function initMap() {
                // The location of Uluru
                  var uluru = {lat: -25.344, lng: 131.036};
                // The map, centered at Uluru
                var map = new google.maps.Map(
                    this.shadowRoot.querySelector('#map'), {zoom: 14, center: uluru});
                // The marker, positioned at Uluru
                var marker = new google.maps.Marker({position: uluru, map: map});
            }
        </script>
        <script async defer
            src="https://maps.googleapis.com/maps/api/js?key=MYAPIKEY&callback=initmap">
        </script>
    `;
    }


}
customElements.define('map-element', MapElement);

输出只是带有地图 ID 的 div 的占位符,我没有收到任何错误。

这是我尝试在我的元素中使用的代码的参考: https ://developers.google.com/maps/documentation/javascript/adding-a-google-map

4

2 回答 2

0

Google Maps API 有一个非常严重的错误,它会一直持续到页面重新加载:https ://issuetracker.google.com/issues/35821412

这会在 SPA 和 Lit 等 Web 组件框架中产生问题,因为地图不包含在组件中,它由事件侦听器附加到窗口。

创建它很容易,但我要补充一点,您要确保 API 每页只加载一次,因此您需要某种已经加载的全局日志。

像这样的东西:

/** URI of the Maps JS library */
const googleMapsApi = `https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=places&callback=initMapApi`;

/** Flag indicating that mapping library is currently being loaded, wait for it to . */
let mapLoading = false;

/** Flag indicating that mapping library has been downloaded. */
let mapReady = false;

// Set window level function for maps API to call back to
(window as any).initMapApi = function () {
    mapReady = true;
    delete (window as any).initMapApi;
}

/** Call this before calling maps API methods to make sure that it's loaded.
 *  @returns {Promise<boolean>} True once loaded. */
export async function mapApiLoaded() {
    if (mapReady)
        return true;

    while (mapLoading)
        await new Promise(requestAnimationFrame);

    if (mapReady)
        return true;

    try {
        mapLoading = true;

        // Get the API key for the current user
        const google = // your key;
        if (!google)
            return;

        const mapApiUri = googleMapsApi.replace('API_KEY', google);

        // Add a <script> tag pointing to the API with the key
        await new Promise((resolve, reject) => {
            const script = document.createElement('script') as HTMLScriptElement;
            script.type = 'text/javascript';
            script.onload = resolve;
            script.onerror = reject;
            document.getElementsByTagName('head')[0].appendChild(script);
            script.src = mapApiUri;
        });

        // Wait for the script to fire the callback method
        while (!mapReady)
            await new Promise(requestAnimationFrame);

        console.log('️ API loaded.');
    }
    finally { mapLoading = false; }

    return mapReady;
}

一旦你有了它,你就可以在每个地图组件的firstUpdated或中调用它connectedCallback

但是,由于内存泄漏,如果您创建大量此类映射并拥有一个单页应用程序,您将很快遇到问题 - 您传递给的每个元素new google.maps.Map都会附加到窗口,直到用户刷新,并且没有办法断开它。

您仍然可以将 Google Maps API 与 SPA 一起使用,但您必须重复使用地图实例......

  • connectedCallback检查现有地图元素并在未连接时重新使用。
  • disconnectedCallback明确的路线/标记/等上并缓存地图以供重用。

所以你的代码最终是这样的:

@customElement('map-element')
class MapElement 
    extends LitElement {
    render() {
        return html `<div id="map"></div>`;
    }

    @query('#map')
    private mapWrapper: HTMLDivElement;

    private map: google.maps.Map;

    async connectedCallback() {
        await mapApiLoaded();
        
        const oldMap = window.oldMaps.pop() // Or however you get your previously used maps 
        if (!oldMap)
            // Create new map in your wrapper
            this.map = new google.maps.Map(this.mapWrapper, ...your options);
        else {
            // Reconfig old map and add it to your wrapper
            oldMap.setOptions(...your options);
            this.map = oldMap;
            this.mapWrapper.appendChild(oldMap);
        }
    }

    disconnectedCallback() {
        if(!this.map) return;

        // Remove from shadow DOM
        this.map.remove();

        // Clear markers/routes from this.map
        // Add to register of unused maps, say...
        window.oldMaps.push(this.map);
    }
}
于 2020-07-20T15:25:42.563 回答
0

您可以在connectedCallback方法中添加脚本,以确保在组件包含在 DOM 中时包含它

constructor() {
  this.callbackName = 'initMap';
  this.mapsUrl = https://maps.googleapis.com/maps/api/js?key=MYAPIKEY
}
connectedCallback() {
  window[this.callbackName] = this._initMap.bind(this)
  this._addMapScript() // This
}

addScript: function() {
  var script = document.createElement('script');
  script.src = `${this.mapsUrl}&callback=${this.callbackName}`;
  var s = document.querySelector('script') || document.body;
  s.parentNode.insertBefore(script, s);
}

_initMap() {
  // To be we have shadow DOM ready.
  this.updateCompleted.then(this.initMap);
}

initMap() {
  // Your code here
}

像这样的东西应该有效

于 2019-01-15T06:12:38.087 回答