[How to] load external dependencies (CDN) ASYNC



  • The Problem

    So recently I came upon a problem: I needed to use Google Map API

    The Google Maps API cannot be simply included as a NPM dependency, and you need a key to use it

    Of course I could request it in src/index.html like so

    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    
        <title>My awesome page</title>
        <link rel="icon" href="statics/img/favicon.ico" type="image/x-icon">
      </head>
      <body>
        <div id="q-app"></div>
        <!-- built files will be auto injected -->
    
        <script src="https://maps.googleapis.com/maps/api/js?key=xxxxxxx">
    
      </body>
    </html>
    
    

    This is not ideal

    Say you have a large web app. You will load the google maps api on first load. Even if you only use it in one page of your SPA. This means clients download a library they may not need at a moment they definitely dont need it.

    So add the deffer atribute

    
       <script deffer src="https://maps.googleapis.com/maps/api/js?key=xxxxxxx">
    
    

    This is better

    This means the loading of this library is done last. However clients still download it whether they need it or not.

    Still not ideal

    Ideally we want the api to load when needed, if it is not in memory yet. How do we do this? Using this script:

    // src/externalApi.js
    
    let api = {}
    export default {
      config: function configApi (id, url) {
        if (!api[id]) {
          api[id] = {
            config: {
              id,
              url
            }
          }
        }
        else {
          // atempted creating same api multiple times
          // error code goes here
          console.error(`Atempted to create API ${id} multiple times`)
        }
      },
      load: function loadApi (id) {
        if (api[id]) {
          if (!api[id].promise {
            if (!document.getElementById(api[id].config.id)) {
              api[id].promise = new Promise((resolve, reject) => {
                let script = document.createElement('script')
                script.src = api[id].config.url
                script.async = true
                script.id = api[id].config.id
                script.onload = () => {
                  resolve()
                }
                script.onerror = (err) => {
                  reject(err)
                  // not sure about this part. ESLint wants error to be passed, not sure if doing it properly
                }
                document.body.appendChild(script)
              })
            }
            else {
              // atempted to create api but id already exists
              // error code
             console.error(`Make sure the id ${id} is unique and is not used anywhere else in your app.`)
            }
          })
          return api[id].promise
        }
        else {
          // api has not been configured
          // error
          console.error(`API ${id} does not exist. Have you configured it in main.js?`)
        }
      }
    }
    
    

    What this does

    This allows us to configure our api in main.js, but load it only once requested. If the api has already been loaded, it will not be loaded again.

    Usage:

    
    // main.js
    import externalApi from '~src/externalApi'
    
    externalApi.config('google-api', 'https://maps.googleapis.com/maps/api/js?key=xxxxxxxxxxxxxxxxxxx')
    
    // some component or view
    import { externalApi } from 'quasar'
    
    ...
    externalApi.load('google-api')
    .then(() => {
      console.log('google api is loaded')
    })
    ...
    // allternative
    
    mounted: async function () {
      await externalApi.load()
      console.log('api now loaded')
    }
    
    

    Hope this helps!


Log in to reply
 

Looks like your connection to Quasar Framework was lost, please wait while we try to reconnect.