【问题标题】:How to add external JS scripts to VueJS Components?如何将外部 JS 脚本添加到 VueJS 组件?
【发布时间】:2021-05-14 19:33:19
【问题描述】:

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

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

但是,我不想一开始就加载这些文件。

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

有没有办法做到这一点?

谢谢。

【问题讨论】:

    标签: javascript vue.js vuejs2 vue-component vue-router


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      更新:这在 Vue 3 中不再有效。您将收到此错误:

      VueCompilerError: Tags with side effect (<script> and <style>) are ignored in client component templates.


      如果您尝试将外部 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"See this question to learn more about MIME type for js):

      &lt;script type="application/javascript" defer src="..."&gt;&lt;/script&gt;


      您可能会注意到defer 属性。如果您想了解更多信息,请观看this video by Kyle

      【讨论】:

      • 效果很好,谢谢! Webpack 的附加提示:require('module-name/file-name.js')
      • VueCompilerError: 带有副作用的标签(
      • 谢谢!谁能解释这是如何/为什么起作用的?学习 Vue 所以很好奇。
      • 感谢各位,效果很好,也适用于内联 javascript 代码
      • 它不再起作用了。
      【解决方案3】:

      嗯,这是我在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)
                      }
                  })
              },
          },
      }
      

      【讨论】:

        【解决方案4】:

        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 上使用过这个。这里有一个关于“让 self = this;”这个技巧的解释。在 Javascript 中工作:

        What does 'var that = this;' mean in JavaScript?

        【讨论】:

          【解决方案5】:

          最简单的解决方案是在你的 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>
          

          【讨论】:

            【解决方案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() }
            }
            
            

            【讨论】:

              【解决方案7】:

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

              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 has stopped supporting $on.

              【讨论】:

                【解决方案8】:

                这个用例有一个 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>
                

                【讨论】:

                  【解决方案9】:

                  使用 webpack 和 vue 加载器你可以做这样的事情

                  它在创建组件之前等待外部脚本加载,因此组件中可以使用全局变量等

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

                  【讨论】:

                  • >> "你把这段代码放在哪里?" : 它在你的 vuejs 组件的组件部分。
                  • 如果你必须多次使用这个组件怎么办?您将多次加载脚本。如果是谷歌脚本,它会在你的控制台中抛出警告。
                  • 您能解释一下import(someComponent) 的情况吗?您正在加载的脚本是否必须定义为 es 模块?如果您要导入附加到 window 的脚本,这是否不起作用?
                  【解决方案10】:

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

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

                  【讨论】:

                  • 附带问题:添加属性“defer”是否有意义?我猜不是因为网站已经加载,但也许它仍然有意义......有什么想法吗?
                  【解决方案11】:

                  mounted中create tag的置顶答案很好,但是有一些问题:如果你多次更改链接,它会一遍又一遍地重复create tag。

                  所以我创建了一个脚本来解决这个问题,你可以根据需要删除标签。

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

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

                  【讨论】:

                  • 一旦脚本被加载,它就已经在内存中了。从 dom 中移除它并不会移除它的足迹。
                  【解决方案12】:

                  解决这个问题的一种简单有效的方法是,将外部脚本添加到组件的 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

                  【讨论】:

                  • created() 方法无法获取文档,请改用mounted()
                  • 之后使用脚本定义的变量,即使将 defer 设置为 false,也不起作用。示例:加载api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.jsmapboxgl.accessToken = 123。必须使用 vue-plugin-load-script .then() 才能工作。
                  • 这是包含您无法控制且无法直接包含的外部库的好方法。要访问其中的构造函数和函数,而 Vue 编译器不会抱怨,请参阅 stackoverflow.com/a/62617268/917444
                  • 您好!我试过这个解决方案。我想从项目 src/assets/js/ 文件夹中加载一个脚本,但它有错误 uncaught SyntaxError: Unexpected token '
                  • @mejiamanuel57 如果我要复用这样的 vue 组件呢?每个人都会在文档头部添加相同的
                  【解决方案13】:

                  为了保持组件干净,您可以使用 mixins。

                  在你的组件上导入外部 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",
                         }
                       }
                      ]
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    您可以使用 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 },
                          // ...
                        ]
                      }
                    })
                    

                    查看link 了解更多示例。

                    【讨论】:

                      【解决方案15】:

                      我已经下载了一些带有自定义 js 文件和 jquery 的 HTML 模板。我必须将这些 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")
                      

                      【讨论】:

                      • 这个 Vue.loadScript() 将位于哪个文件中?是App.vue文件吗?
                      【解决方案16】:

                      您是否正在使用其中一种用于 vue (https://github.com/vuejs-templates/webpack) 的 Webpack 入门模板?它已经与 vue-loader (https://github.com/vuejs/vue-loader) 一起设置。如果你没有使用 starter 模板,你必须设置 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

                      【讨论】:

                      【解决方案17】:

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

                      【讨论】:

                      • 这些脚本来自 paypal 和 stripe。我无法下载本地文件
                      • 链接失效
                      • 您可以下载外部脚本、查看源代码、复制/粘贴到您自己的文件中。
                      • @minimallinux 对于 Stripe 和 Paypal,这将违反 PCI-DSS。所以不要那样做。
                      猜你喜欢
                      • 2019-04-12
                      • 2021-03-26
                      • 2022-10-24
                      • 1970-01-01
                      • 1970-01-01
                      • 2022-06-11
                      • 2021-08-26
                      • 2021-10-03
                      相关资源
                      最近更新 更多