[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!
-
@benoitranque thanks, ur code is broken, I just fixed it
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?`) } } }
-
Thanks, where was it broken exactly? This is an old post so I forgot a lot of this, here is a newer post with a solution for 0.15