How to add external JS scripts to VueJS Components?

Javascriptvue.jsVuejs2Vue ComponentVue Router

Javascript Problem Overview


I've to use two external scripts for the payment gateways.

Right now both are put in the index.html file.

However, I don't want to load these files at the beginning itself.

The payment gateway is needed only in when user open a specific component (using router-view).

Is there anyway to achieve this?

Thanks.

Javascript Solutions


Solution 1 - Javascript

A simple and effective way to solve this, it's by adding your external script into the vue mounted() of your component. I will illustrate you with the Google Recaptcha script:

<template>
   .... your HTML
</template>

<script>
  export default {
    data: () => ({
      ......data of your component
    }),
    mounted() {
      let recaptchaScript = document.createElement('script')
      recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js')
      document.head.appendChild(recaptchaScript)
    },
    methods: {
      ......methods of your component
    }
  }
</script>

Source: https://medium.com/@lassiuosukainen/how-to-include-a-script-tag-on-a-vue-component-fe10940af9e8

Solution 2 - Javascript

I have downloaded some HTML template that comes with custom js files and jquery. I had to attach those js to my app. and continue with Vue.

Found this plugin, it's a clean way to add external scripts both via CDN and from static files https://www.npmjs.com/package/vue-plugin-load-script

// local files
// you have to put your scripts into the public folder. 
// that way webpack simply copy these files as it is.
Vue.loadScript("/js/jquery-2.2.4.min.js")

// cdn
Vue.loadScript("https://maps.googleapis.com/maps/api/js")

Solution 3 - Javascript

using webpack and vue loader you can do something like this

it waits for the external script to load before creating the component, so globar vars etc are available in the component

components: {
 SomeComponent: () => {
  return new Promise((resolve, reject) => {
   let script = document.createElement('script')
   script.onload = () => {
    resolve(import(someComponent))
   }
   script.async = true
   script.src = 'https://maps.googleapis.com/maps/api/js?key=APIKEY&libraries=places'
   document.head.appendChild(script)
  })
 }
},

Solution 4 - Javascript

UPDATE: This no longer works in Vue 3. You will receive this error:

> VueCompilerError: Tags with side effect (

Solution 5 - Javascript

This can be simply done like this.

  created() {
    var scripts = [
      "https://cloudfront.net/js/jquery-3.4.1.min.js",
      "js/local.js"
    ];
    scripts.forEach(script => {
      let tag = document.createElement("script");
      tag.setAttribute("src", script);
      document.head.appendChild(tag);
    });
  }

Solution 6 - Javascript

You can load the script you need with a promise based solution:

export default {
  data () {
    return { is_script_loading: false }
  },
  created () {
    // If another component is already loading the script
    this.$root.$on('loading_script', e => { this.is_script_loading = true })
  },
  methods: {
    load_script () {
      let self = this
      return new Promise((resolve, reject) => {

        // if script is already loading via another component
        if ( self.is_script_loading ){
          // Resolve when the other component has loaded the script
          this.$root.$on('script_loaded', resolve)
          return
        }

        let script = document.createElement('script')
        script.setAttribute('src', 'https://www.google.com/recaptcha/api.js')
        script.async = true
        
        this.$root.$emit('loading_script')

        script.onload = () => {
          /* emit to global event bus to inform other components
           * we are already loading the script */
          this.$root.$emit('script_loaded')
          resolve()
        }

        document.head.appendChild(script)

      })

    },
  
    async use_script () {
      try {
        await this.load_script()
        // .. do what you want after script has loaded
      } catch (err) { console.log(err) }

    }
  }
}

Please note that this.$root is a little hacky and you should use a vuex or eventHub solution for the global events instead.

You would make the above into a component and use it wherever needed, it will only load the script when used.

NOTE: This is a Vue 2.x based solution. Vue 3 has stopped supporting $on.

Solution 7 - Javascript

Are you using one of the Webpack starter templates for vue (https://github.com/vuejs-templates/webpack)? It already comes set up with vue-loader (https://github.com/vuejs/vue-loader). If you're not using a starter template, you have to set up webpack and vue-loader.

You can then import your scripts to the relevant (single file) components. Before that, you have toexport from your scripts what you want to import to your components.

ES6 import:

Edit
You can import from these wrappers:

Solution 8 - Javascript

You can use the vue-head package to add scripts, and other tags to the head of your vue component.

Its as simple as:

var myComponent = Vue.extend({
  data: function () {
    return {
      ...
    }
  },
  head: {
    title: {
      inner: 'It will be a pleasure'
    },
    // Meta tags
    meta: [
      { name: 'application-name', content: 'Name of my application' },
      { name: 'description', content: 'A description of the page', id: 'desc' }, // id to replace intead of create element
      // ...
      // Twitter
      { name: 'twitter:title', content: 'Content Title' },
      // with shorthand
      { n: 'twitter:description', c: 'Content description less than 200 characters'},
      // ...
      // Google+ / Schema.org
      { itemprop: 'name', content: 'Content Title' },
      { itemprop: 'description', content: 'Content Title' },
      // ...
      // Facebook / Open Graph
      { property: 'fb:app_id', content: '123456789' },
      { property: 'og:title', content: 'Content Title' },
      // with shorthand
      { p: 'og:image', c: 'https://example.com/image.jpg' },
      // ...
    ],
    // link tags
    link: [
      { rel: 'canonical', href: 'http://example.com/#!/contact/', id: 'canonical' },
      { rel: 'author', href: 'author', undo: false }, // undo property - not to remove the element
      { rel: 'icon', href: require('./path/to/icon-16.png'), sizes: '16x16', type: 'image/png' }, 
      // with shorthand
      { r: 'icon', h: 'path/to/icon-32.png', sz: '32x32', t: 'image/png' },
      // ...
    ],
    script: [
      { type: 'text/javascript', src: 'cdn/to/script.js', async: true, body: true}, // Insert in body
      // with shorthand
      { t: 'application/ld+json', i: '{ "@context": "http://schema.org" }' },
      // ...
    ],
    style: [
      { type: 'text/css', inner: 'body { background-color: #000; color: #fff}', undo: false },
      // ...
    ]
  }
})

Check out this link for more examples.

Solution 9 - Javascript

The top answer of create

Solution 10 - Javascript

The answer from mejiamanuel57 is great, but I want to add a couple of tips that work in my case (I spent some hours on them). First, I needed to use the "window" scope. Also, if you need to access any Vue element inside the "onload" function, you need a new variable for the "this" instance.

<script>
import { mapActions } from "vuex";
export default {
  name: "Payment",
  methods: {
    ...mapActions(["aVueAction"])
  },
  created() {
    let paywayScript = document.createElement("script");
    let self = this;
    paywayScript.onload = () => {
      // call to Vuex action.
      self.aVueAction();
      // call to script function
      window.payway.aScriptFunction();
    };
    // paywayScript.async = true;
    paywayScript.setAttribute(
      "src",
      "https://api.payway.com.au/rest/v1/payway.js"
    );
    document.body.appendChild(paywayScript);
  }
};
</script>

I worked with this on Vue 2.6. Here there is an explanation about how the trick "let self = this;" works in Javascript:

https://stackoverflow.com/questions/4886632/what-does-var-that-this-mean-in-javascript

Solution 11 - Javascript

Simplest solution is to add the script in the index.html file of your vue-project

index.html:

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="utf-8">
      <title>vue-webpack</title>
   </head>
   <body>
      <div id="app"></div>
      <!-- start Mixpanel --><script type="text/javascript">(function(c,a){if(!a.__SV){var b=window;try{var d,m,j,k=b.location,f=k.hash;d=function(a,b){return(m=a.match(RegExp(b+"=([^&]*)")))?m[1]:null};f&&d(f,"state")&&(j=JSON.parse(decodeURIComponent(d(f,"state"))),"mpeditor"===j.action&&(b.sessionStorage.setItem("_mpcehash",f),history.replaceState(j.desiredHash||"",c.title,k.pathname+k.search)))}catch(n){}var l,h;window.mixpanel=a;a._i=[];a.init=function(b,d,g){function c(b,i){var a=i.split(".");2==a.length&&(b=b[a[0]],i=a[1]);b[i]=function(){b.push([i].concat(Array.prototype.slice.call(arguments,
         0)))}}var e=a;"undefined"!==typeof g?e=a[g]=[]:g="mixpanel";e.people=e.people||[];e.toString=function(b){var a="mixpanel";"mixpanel"!==g&&(a+="."+g);b||(a+=" (stub)");return a};e.people.toString=function(){return e.toString(1)+".people (stub)"};l="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
         for(h=0;h<l.length;h++)c(e,l[h]);var f="set set_once union unset remove delete".split(" ");e.get_group=function(){function a(c){b[c]=function(){call2_args=arguments;call2=[c].concat(Array.prototype.slice.call(call2_args,0));e.push([d,call2])}}for(var b={},d=["get_group"].concat(Array.prototype.slice.call(arguments,0)),c=0;c<f.length;c++)a(f[c]);return b};a._i.push([b,d,g])};a.__SV=1.2;b=c.createElement("script");b.type="text/javascript";b.async=!0;b.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?
         MIXPANEL_CUSTOM_LIB_URL:"file:"===c.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";d=c.getElementsByTagName("script")[0];d.parentNode.insertBefore(b,d)}})(document,window.mixpanel||[]);
         mixpanel.init("xyz");
      </script><!-- end Mixpanel -->
      <script src="/dist/build.js"></script>
   </body>
</html>

Solution 12 - Javascript

With Vue 3, I use mejiamanuel57 answer with an additional check to ensure the script tag hasn't been loaded already.

    mounted() {
    	const scripts = [
    		"js/script1.js",
    		"js/script2.js"
    	];
    	scripts.forEach(script => {
    		let tag = document.head.querySelector(`[src="${ script }"`);
    		if (!tag) {
    			tag = document.createElement("script");
    			tag.setAttribute("src", script);
    			tag.setAttribute("type", 'text/javascript');
    			document.head.appendChild(tag);	
    		}
    	});
    // ...

Solution 13 - Javascript

To keep clean components you can use mixins.

On your component import external mixin file.

> Profile.vue

import externalJs from '@client/mixins/externalJs';

export default{
  mounted(){
    this.externalJsFiles();
  }
}

> externalJs.js

import('@JSassets/js/file-upload.js').then(mod => {
  // your JS elements 
})

> babelrc (I include this, if any get stuck on import)

{
  "presets":["@babel/preset-env"],
  "plugins":[
    [
     "module-resolver", {
       "root": ["./"],
       alias : {
         "@client": "./client",
         "@JSassets": "./server/public",
       }
     }
    ]
}

Solution 14 - Javascript

mounted() {
    if (document.getElementById('myScript')) { return }
    let src = 'your script source'
	let script = document.createElement('script')
	script.setAttribute('src', src)
	script.setAttribute('type', 'text/javascript')
	script.setAttribute('id', 'myScript')
	document.head.appendChild(script)
}


beforeDestroy() {
	let el = document.getElementById('myScript')
	if (el) { el.remove() }
}

Solution 15 - Javascript

You can use vue-loader and code your components in their own files (Single file components). This will allow you to include scripts and css on a component basis.

Solution 16 - Javascript

well, this is my practice in qiokian (a live2d anime figure vuejs component):

(below is from the file src/qiokian.vue)

<script>
export default {
	data() {
		return {
			live2d_path:
				'https://cdn.jsdelivr.net/gh/knowscount/live2d-widget@latest/',
			cdnPath: 'https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/',
		}
	},
<!-- ... -->
		loadAssets() {
			// load waifu.css live2d.min.js waifu-tips.js
			if (screen.width >= 768) {
				Promise.all([
					this.loadExternalResource(
						this.live2d_path + 'waifu.css',
						'css'
					),
<!-- ... -->
        loadExternalResource(url, type) {
			// note: live2d_path parameter should be an absolute path
			// const live2d_path =
			//   "https://cdn.jsdelivr.net/gh/knowscount/live2d-widget@latest/";
			//const live2d_path = "/live2d-widget/";
			return new Promise((resolve, reject) => {
				let tag

				if (type === 'css') {
					tag = document.createElement('link')
					tag.rel = 'stylesheet'
					tag.href = url
				} else if (type === 'js') {
					tag = document.createElement('script')
					tag.src = url
				}
				if (tag) {
					tag.onload = () => resolve(url)
					tag.onerror = () => reject(url)
					document.head.appendChild(tag)
				}
			})
		},
    },
}

Solution 17 - Javascript

There is a vue component for this usecase

https://github.com/TheDynomike/vue-script-component#usage

<template>
    <div>
        <VueScriptComponent script='<script type="text/javascript"> alert("Peekaboo!"); </script>'/>
    <div>
</template>

<script>
import VueScriptComponent from 'vue-script-component'

export default {
  ...
  components: {
    ...
    VueScriptComponent
  }
  ...
}
</script>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGijo VargheseView Question on Stackoverflow
Solution 1 - Javascriptmejiamanuel57View Answer on Stackoverflow
Solution 2 - JavascriptPJ3View Answer on Stackoverflow
Solution 3 - Javascriptmons droidView Answer on Stackoverflow
Solution 4 - JavascriptRolandView Answer on Stackoverflow
Solution 5 - JavascriptAwais JameelView Answer on Stackoverflow
Solution 6 - JavascripthitautodestructView Answer on Stackoverflow
Solution 7 - Javascriptba_ulView Answer on Stackoverflow
Solution 8 - JavascriptpamekarView Answer on Stackoverflow
Solution 9 - JavascriptoraantView Answer on Stackoverflow
Solution 10 - JavascriptAlexView Answer on Stackoverflow
Solution 11 - JavascriptTanya TalwarView Answer on Stackoverflow
Solution 12 - JavascriptPaulView Answer on Stackoverflow
Solution 13 - JavascriptTushar RoyView Answer on Stackoverflow
Solution 14 - JavascriptMAWView Answer on Stackoverflow
Solution 15 - JavascriptDaniel DView Answer on Stackoverflow
Solution 16 - JavascriptKnowsCountView Answer on Stackoverflow
Solution 17 - JavascriptDynomikeView Answer on Stackoverflow