Как добавить внешние JS-скрипты в компоненты VueJS

Я должен использовать два внешних скрипта для платежных шлюзов. Прямо сейчас оба помещены в файл index.html. Однако я не хочу загружать эти файлы в самом начале. Платежный шлюз необходим только в том случае, когда пользователь открывает определенный компонент (используя роутер-просмотр). Есть ли способ добиться этого?

22 ответа

Решение

A simple and effective way to solve this, it's by adding your external script into the vue mounted() вашего компонента. Я проиллюстрирую вас сценарием Google Recaptcha:

<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

Я скачал некоторый шаблон HTML, который поставляется с пользовательскими файлами js и jquery. Я должен был прикрепить эти JS к моему приложению. и продолжить с Vue.

Нашел этот плагин, это чистый способ добавить внешние скрипты как через CDN, так и из статических файлов 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")

Используя webpack и vue loader, вы можете сделать что-то вроде этого

он ожидает загрузки внешнего скрипта перед созданием компонента, поэтому в компоненте доступны глобальные переменные и т. д.

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

Если вы пытаетесь встроить внешние js-скрипты в шаблон компонента vue.js, выполните следующие действия:

Я хотел добавить в свой компонент внешний код для встраивания javascript, например:

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

И Vue показал мне эту ошибку:

Шаблоны должны отвечать только за отображение состояния в пользовательский интерфейс. Избегайте размещения тегов с побочными эффектами в ваших шаблонах, например, поскольку они не будут анализироваться.


Я решил это, добавив type="application/javascript"( См. Этот вопрос, чтобы узнать больше о типе MIME для js):

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


Вы можете заметить deferатрибут. Если вы хотите узнать больше, посмотрите это видео Кайла.

Это можно просто сделать вот так.

  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 я использую ответ 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); 
            }
        });
    // ...

Вы можете загрузить нужный скрипт с помощью решения на основе обещаний:

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 является немного хакерским, и вы должны использовать вместо этого решение vuex или eventHub для глобальных событий.

Вы могли бы превратить вышесказанное в компонент и использовать его там, где это необходимо, он будет загружать скрипт только при использовании.

Вы можете использовать пакет 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 },
      // ...
    ]
  }
})

Проверьте эту ссылку для большего количества примеров.

Быстрый и простой способ, который я нашел, выглядит следующим образом:

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

Используете ли вы один из начальных шаблонов Webpack для vue ( https://github.com/vuejs-templates/webpack)? Он уже настроен с помощью vue-loader ( https://github.com/vuejs/vue-loader). Если вы не используете стартовый шаблон, вам нужно настроить webpack и vue-loader.

Вы можете тогда import ваши скрипты для соответствующих (один файл) компонентов. До этого вы должныexport из ваших сценариев, что вы хотите import к вашим компонентам.

ES6 импорт:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
- http://exploringjs.com/es6/ch_modules.html

~ Изменить ~
Вы можете импортировать из этих упаковщиков:
- https://github.com/matfish2/vue-stripe
- https://github.com/khoanguyen96/vue-paypal-checkout

Верхний ответ на создание тега в смонтированном - это хорошо, но с ним есть некоторые проблемы: если вы измените ссылку несколько раз, она будет повторять создание тега снова и снова.

Поэтому я создал сценарий для решения этой проблемы, и вы можете удалить тег, если хотите.

Это очень просто, но вы можете сэкономить ваше время, создав его самостоятельно.

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

......

Самое простое решение - добавить скрипт в index.html файл вашего vue-проекта

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>

Ответ от mejiamanuel57 отличный, но я хочу добавить пару советов, которые работают в моем случае (я потратил на них несколько часов). Во-первых, мне нужно было использовать «оконную» область видимости. Кроме того, если вам нужно получить доступ к любому элементу Vue внутри функции «onload», вам понадобится новая переменная для экземпляра «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.

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

Если вы используете Vue 3 и Composition API (я настоятельно рекомендую), и вы используете<script>тегов много, вы можете написать для этого "составную" функцию:

      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функция.

Чтобы компоненты оставались чистыми, можно использовать миксины.

На вашем компоненте импортируйте внешний файл миксина.

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",
       }
     }
    ]
}

Вы можете использовать vue-loader и кодировать свои компоненты в свои собственные файлы (Single file components). Это позволит вам включать скрипты и CSS на основе компонентов.

ну, это моя практика в qiokian (компонент vuejs live2d anime figure):

(ниже из файла 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.js 3, вы можете использовать useScriptTag из библиотеки vue-use для простой загрузки внешних скриптов, это дает вам простой и настраиваемый API для загрузки, выгрузки и использования событий обратного вызова.

      import { useScriptTag } from '@vueuse/core'

useScriptTag(
  'https://player.twitch.tv/js/embed/v1.js',
  // on script tag loaded.
  (el: HTMLScriptElement) => {
    // do something
  },
)

Если вы пытаетесь использовать переменную, определенную в файле JavaScript, который загружается асинхронно, и вы получаете сообщение об ошибке «undefined», скорее всего, это связано с тем, что загрузка сценария еще не завершена. Чтобы решить эту проблему, вы можете использовать функцию onload, чтобы убедиться, что сценарий завершил загрузку, прежде чем пытаться получить доступ к переменной. В качестве примера:

      const script = document.createElement(...) // set attribute and so on...
script.onload = () => {
  // do something with some variables/functions/logic from the loaded script
}

Вы можете использовать свойство head в vue, чтобы вернуть пользовательский скрипт.

      
export default {
    head: {
        script: [{
            src: 'https://cdn.polygraph.net/pg.js',
                type: 'text/javascript'
        }]
    }
}

Для этого варианта использования есть компонент 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>
Другие вопросы по тегам