如何将外部JS脚本添加到VueJS组件?

我必须为支付网关使用两个外部脚本。

现在两者都放在index.html文件中。

但是,我不想在开头本身就加载这些文件。

支付网关仅在用户打开特定组件(using router-view)时才需要。

有办法实现这个目标吗?

谢谢。

391472 次浏览

你可以使用vue-loader并在它们自己的文件中编写组件(单文件组件)。这将允许您在组件的基础上包含脚本和css。

你是否正在使用vue (https://github.com/vuejs-templates/webpack)的Webpack入门模板之一?它已经通过vue-loader (https://github.com/vuejs/vue-loader)设置好了。如果你不使用启动器模板,你必须设置webpack和vue-loader。

然后你可以import你的脚本到相关的(单文件)组件。在此之前,你必须从你的脚本中__abc1你想要import到你的组件。

< p > ES6进口:< br > ——https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import < br > ——http://exploringjs.com/es6/ch_modules.html < / p > < br > < p > ~编辑~ 你可以从这些包装器中导入:
——https://github.com/matfish2/vue-stripe < br > - https://github.com/khoanguyen96/vue-paypal-checkout

解决这个问题的一个简单而有效的方法是将外部脚本添加到组件的vue mounted()中。我将用谷歌说的脚本来说明:

<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>

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

使用webpack和vue加载器,你可以这样做

在创建组件之前,它会等待外部脚本加载,因此在组件中可以使用globar vars等

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)
  })
}
},

你可以加载你需要的基于承诺的解决方案的脚本:

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) }


}
}
}

请注意,this.$root有点笨拙,你应该使用vuexeventHub解决方案来代替全局事件。

你可以把上面的内容变成一个组件,并在任何需要的地方使用它,它只会在使用时加载脚本。

请注意:这是一台Vue 2。基于X的解。Vue 3已停止支持$on

我下载了一些HTML模板,附带自定义js文件和jquery。我必须将这些js附加到我的应用程序,并继续使用Vue。

发现这个插件,这是一个干净的方式来添加外部脚本通过CDN和静态文件 https://www.npmjs.com/package/vue-plugin-load-script < / p >
// 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")

你可以使用vue-head包将脚本和其他标签添加到vue组件的头部。

很简单:

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 },
// ...
]
}
})

查看这个链接来获得更多的例子。

顶部的创建标签的答案是好的,但它有一些问题:如果你改变你的链接多次,它会重复创建标签一遍又一遍。

所以我创建了一个脚本来解决这个问题,如果你愿意,你可以删除标签。

它非常简单,但可以节省你自己创建的时间。

// PROJECT/src/assets/external.js


function head_script(src) {
if(document.querySelector("script[src='" + src + "']")){ return; }
let script = document.createElement('script');
script.setAttribute('src', src);
script.setAttribute('type', 'text/javascript');
document.head.appendChild(script)
}


function body_script(src) {
if(document.querySelector("script[src='" + src + "']")){ return; }
let script = document.createElement('script');
script.setAttribute('src', src);
script.setAttribute('type', 'text/javascript');
document.body.appendChild(script)
}


function del_script(src) {
let el = document.querySelector("script[src='" + src + "']");
if(el){ el.remove(); }
}




function head_link(href) {
if(document.querySelector("link[href='" + href + "']")){ return; }
let link = document.createElement('link');
link.setAttribute('href', href);
link.setAttribute('rel', "stylesheet");
link.setAttribute('type', "text/css");
document.head.appendChild(link)
}


function body_link(href) {
if(document.querySelector("link[href='" + href + "']")){ return; }
let link = document.createElement('link');
link.setAttribute('href', href);
link.setAttribute('rel', "stylesheet");
link.setAttribute('type', "text/css");
document.body.appendChild(link)
}


function del_link(href) {
let el = document.querySelector("link[href='" + href + "']");
if(el){ el.remove(); }
}


export {
head_script,
body_script,
del_script,
head_link,
body_link,
del_link,
}

你可以这样使用它:

// PROJECT/src/views/xxxxxxxxx.vue


......


<script>
import * as external from '@/assets/external.js'
export default {
name: "xxxxxxxxx",
mounted(){
external.head_script('/assets/script1.js');
external.body_script('/assets/script2.js');
external.head_link('/assets/style1.css');
external.body_link('/assets/style2.css');
},
destroyed(){
external.del_script('/assets/script1.js');
external.del_script('/assets/script2.js');
external.del_link('/assets/style1.css');
external.del_link('/assets/style2.css');
},
}
</script>


......

为了保持组件的干净,你可以使用mixin。

在组件上导入外部mixin文件。

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(我包括这个,如果任何导入卡住)

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

这可以像这样简单地完成。

  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);
});
}

更新:这不再工作在Vue 3。你会收到这样的错误:

VueCompilerError:在客户端组件模板中忽略带有副作用(和)的标签。


如果你试图将外部js脚本嵌入vue.js组件模板,请遵循以下步骤:

我想像这样向我的组件添加外部JavaScript嵌入代码:

<template>
<div>
This is my component
<script src="https://badge.dimensions.ai/badge.js"></script>
</div>
<template>

Vue显示了这个错误:

模板应该只负责将状态映射到UI。避免在模板中放置带有副作用的标记,例如,因为它们不会被解析。


我解决它的方法是添加type="application/javascript" (查看这个问题,了解更多关于js的MIME类型):

<script type="application/javascript" defer src="..."></script>


你可能会注意到defer属性。如果你想了解更多,请观看这个由Kyle拍摄的视频

最简单的解决方案是将脚本添加到vue-project的index.html文件中

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>

这个用例有一个vue组件

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>
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() }
}


来自mejiamanuel57的答案很好,但我想添加几个适合我的技巧(我花了几个小时在上面)。首先,我需要使用“窗口”;范围。此外,如果您需要访问“onload”中的任何Vue元素;函数中,您需要为“this”添加一个新变量。实例。

<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>

我在Vue 2.6上使用它。这里有一个关于技巧&;let self = this;&;工作在Javascript:

'var that = this;'JavaScript的意思?< / >

好吧,这是我在qiokian(一个live2d动画人物vuejs组件)中的实践:

(下面是来自文件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)
}
})
},
},
}

在Vue 3中,我使用mejiamanuel57回答并附加检查,以确保脚本标记尚未加载。

    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);
}
});
// ...

如果你正在使用Vue 3和Composition API(我强烈推荐),并且你经常使用<script>标记,你可以编写一个&;composable"函数如下:

import { onMounted } from "vue";


export const useScript = (src, async = false, defer = false) => {
onMounted(() => {
// check if script already exists
if (document.querySelector(`head script[src="${src}"`)) return;


// add tag to head
const tag = document.createElement("script");
tag.setAttribute("src", src);
if (async) tag.setAttribute("async", "");
if (defer) tag.setAttribute("defer", "");
tag.setAttribute("type", "text/javascript");
document.head.append(tag);
});
};

或者,如果你正在使用VueUse(我也强烈推荐),你可以使用他们现有的useScriptTag函数。