Loading google maps on onload event still gives lighthouse message to load it after the page has loaded

Viewed 1046

I have ran the chrome audit for my project, where I was suggested to reduce the impact of third-party code. In my case I am loading google maps api. I am doing that like this:

In my main.js I check for the element to attach the map:

if (document.querySelector('.js-map')) {
  GMap.GMap('.js-map', process.env.GOOGLE_MAPS_API_KEY);
}

GMap looks like this:

export function GMap(el, apiKey) {

  const gApiKey = apiKey
  const gmapApi = new GoogleMapsApi(gApiKey)
  const mapEl   = document.querySelector(el)
  const data    = {
    lat:     parseFloat(mapEl.dataset.lat ? mapEl.dataset.lat : 0),
    lng:     parseFloat(mapEl.dataset.lng ? mapEl.dataset.lng : 0),
    zoom:    parseFloat(mapEl.dataset.zoom ? mapEl.dataset.zoom: 12),
  }
  // Call map renderer
  gmapApi.load().then(() => {
    renderMap(mapEl, data);
    document.querySelector('.static-map-img').setAttribute("style", "display:none;");
    document.querySelector('.map').removeAttribute('style');
 })
}

And this is the GoogleMapsApi:

 /**
 * GoogleMapsApi
 * Class to load google maps api with api key
 * and global Callback to init map after resolution of promise.
 *
 * @exports {GoogleMapsApi}
 * @example MapApi = new GoogleMapsApi();
 *          MapApi.load().then(() => {});
 */
class GoogleMapsApi {

  /**
   * Constructor
   * @property {string} apiKey
   * @property {string} callbackName
   */
  constructor(gApiKey) {

    // api key for google maps
    this.apiKey = gApiKey;

    // Set global callback
    if (!window._GoogleMapsApi) {
      this.callbackName = '_GoogleMapsApi.mapLoaded';
      window._GoogleMapsApi = this;
      window._GoogleMapsApi.mapLoaded = this.mapLoaded.bind(this);
    }
  }

  /**
   * Load
   * Create script element with google maps
   * api url, containing api key and callback for
   * map init.
   * @return {promise}
   * @this {_GoogleMapsApi}
   */
  load() {
    if (!this.promise) {
      this.promise = new Promise(resolve => {
        this.resolve = resolve;

        if (typeof window.google === 'undefined') {
          const script = document.createElement('script');
          script.src = `//maps.googleapis.com/maps/api/js?key=${this.apiKey}&callback=${this.callbackName}`;
          script.async = 'true';
          script.defer = 'true';
          script.setAttribute('rel', 'preload');
          document.body.append(script);
        } else {
          this.resolve();
        }
      });
    }

    return this.promise;
  }

  /**
   * mapLoaded
   * Global callback for loaded/resolved map instance.
   * @this {_GoogleMapsApi}
   *
   */
  mapLoaded() {

    if (this.resolve) {
      this.resolve();
    }
  }
}

export default GoogleMapsApi;

After running the audit I have changed the loading of the map to happen after the page has loaded:

window.onload = function () {
  if (document.querySelector('.js-map')) {
    GMap.GMap('.js-map', process.env.GOOGLE_MAPS_API_KEY);
  }
};

But, I still get the same audit message from chrome's lighthouse:

enter image description here

You can see a working example here.

Update

I have also tried to load the map with async await logic like this:

const loadMap = async () => {
  if (document.querySelector('.js-map')) {
    setTimeout(async () => {
      const GMap = await import('./GMap/index');
      GMap.default('.js-map', process.env.GOOGLE_MAPS_API_KEY)
    }, 0);
  }
}

async function concurrentStart() {
  const map = loadMap();
  InfoComponent.init();
  SelectBuilder.init();
  FilterBuilder.init();
  window.onload = async function () {
    const lightButton = document.querySelector('.light-icon');
    const changeTheme = await import('./themeSelector');
    lightButton.addEventListener('click', changeTheme.default, { capture: true, passive: true });  
  };

  await map;
}
concurrentStart();

But, I kept getting the same result when running lighthouse audit. What is the right way of loading the map without blocking the rest of the app's code?

1 Answers

Lighthouse will wait until there is no a single task in the event loop, so the longer you set the setTimeout the lower rank you'll get. Check this out

I'd just let the browser do the dirty work. Just add the script, and use it's async / defer attributes

Here you go with more information about async / defer and about the event loop

Consider using other performance tools as well, like https://developers.google.com/speed and https://www.webpagetest.org/

If it is critical, you could load the lib only when it is visible by using scroll events or intersection observer but it is likely that lighthouse won't feel any improvements, the online tools above will do.

Related