5

I'm using Webpack and VueJs 2. I want to use a 3rd party javascript library in my component, such as this:

<script async defer src="https://apis.google.com/js/api.js" ... ></script>

I found an article here about how to do this for npm packages, but that doesn't work for me since this library is not available as npm package.

It's not possible for me to download the file locally and use it since the library might change and stop working. Therefore it has to be loaded from the link every time the page is loaded by the browser.

I found one possible solution here but that is basically a hack(modify dom to add a script element after document is loaded)

I believe there must be a simple good practice solution for this issue since I assume this is a common use-case.

Update: If I put the script inside head tags in my index file, it would be loaded for all the components. For performance reasons, I would want it to be loaded only for a certain component.

Caner
  • 57,267
  • 35
  • 174
  • 180
  • Why don't you just include the script as seen in your example and instantiate it, if it needs to be .. inside the created/method hooks? – samayo Mar 19 '18 at 15:02
  • Why dont you put your script tag inside the of your index.html file ? – Loïc Monard Mar 19 '18 at 15:03
  • @LoïcMonard see update – Caner Mar 19 '18 at 15:06
  • if you don't want to include it in your index file you can still include it inside a single component so it will only be loaded when that component is loaded – samayo Mar 19 '18 at 15:13
  • @samayo can you give an example how to do that? – Caner Mar 19 '18 at 15:15
  • This is as good as it gets: https://stackoverflow.com/a/49331651/1850609 - The differences you talk about (*"I would want it to be loaded only for a certain component."*) are not technically possible. These external scripts usually modify the (add vars to) `window` object. You can't *un*-modify it without knowing the internals of the script (so there's no general solution for this problem). If there were a good enough idea, even if the loader didn't exist, I'd implement it myself (your request is very common!), but, unfortunately, there isn't a good one-size-fits-all solution. – acdcjunior Mar 19 '18 at 15:28

2 Answers2

7

As far as I know, there is no way you could use something better than adding the script tag into the head of your page dynamically. However, you could create a little plugin to handle that for you, and trigger the script loading only in the components you want, and just once.

Let's begin with the plugin itself:

import Vue from 'vue'

const scriptLoader = {
    loaded: [],
    load (src) {
        if (this.loaded.indexOf(src) !== -1) {
            return
        }

        this.loaded.push(src)

        if (document) {
            const script = document.createElement('script')
            script.setAttribute('src', src)
            document.head.appendChild(script)
        }
    }
}

Vue.use({
    install () {
        Vue.prototype.$scriptLoader = scriptLoader
    }
})

As you can see, you create an object scriptLoader that contains some sort of cache object loaded that will remember what script you already loaded at some point in your application. It also contains a load() method that will handle the adding of the script into your head.

The condition if (document) is here in case you would server side render your app, document would not exist.

You could then load your script into any of your component at creation using the created hook:

export default {
    ...
    created () {
        this.$scriptLoader.load('https://apis.google.com/js/api.js')
    }
}

This is the cleanest way I came with to handle that kind of script... I hope this can help...

Hammerbot
  • 15,696
  • 9
  • 61
  • 103
1

Ok, found a solution that satisfies all the requirements.

  • Script is downloaded from web, no npm package is needed
  • Script is only loaded for certain components when needed
  • No ugly dom-manipulation

Add the script inside index.html with v-if directive

<script v-if="loadGoogleAPI" async defer src="https://apis.google.com/js/api.js" ... ></script>

Inside the component file (.vue), if you want the script to be loaded set the flag to true:

<script>
export default {
  ...
  loadGoogleAPI: true,
  data() {
  ...
  }
};
</script>
Caner
  • 57,267
  • 35
  • 174
  • 180
  • If the `script` with the `v-if` is outside the Vue instance, the `v-if` has no effect at all. – acdcjunior Mar 19 '18 at 16:09
  • Even if the `v-if` did work, removing the `script` has too no effect to the code that was already loaded. Removing the ` – acdcjunior Mar 19 '18 at 16:19
  • @acdcjunior It's a good point, but I luckily don't need to unload the script. If I needed one work-around would be to make a multi-page vue app – Caner Mar 19 '18 at 16:54
  • Then the other question/answer I pointed do exactly the same thing. – acdcjunior Mar 19 '18 at 16:55