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

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

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

只有在用户打开特定组件(使用路由器视图)时才需要支付网关。

有办法实现这个目标吗?

谢谢。


当前回答

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

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

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

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

......

其他回答

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

如果你正在使用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函数。

我发现一个快速简单的方法是这样的:

<template>
    <div>Some HTML</div>
    <component
        src="https://unpkg.com/flowbite@1.5.3/dist/flowbite.js"
        :is="'script'"
    ></component>
</template>

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

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

    }
  }
}

请注意这一点。$root有点粗糙,对于全局事件,您应该使用vuex或甚至enthub解决方案。

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

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

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