在刺激框架中从 API 访问惰性全局变量

Accessing lazy global variable from API in stimulus framework

我正在尝试创建一个 Stimulus controller which would access global Youtube Player API 变量

Youtube Player API 延迟加载 - class 加载 异步

// 2. This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

问题是,Stimulus 不想访问 YT 变量。

我尝试创建一个加载脚本的函数,然后运行给定的函数,并将其导入到 stimulus

export function loadScript (url, callback) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    if (script.readyState) { // only required for IE <9
        script.onreadystatechange = function () {
            if (script.readyState === "loaded" || script.readyState === "complete") {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //Others
        script.onload = function () {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

但是如您所见,它只是创建了新的脚本对象,所以 Stimulus 仍然无法访问它。

有没有办法让 Stimulus 访问加载的脚本以及其中声明的变量?

这是一个完整的控制器代码(不要介意不好的做法,这只是一个快速尝试):

import {
    Controller
} from 'stimulus';

import $ from 'jquery';

export default class extends Controller {
    static targets = ['collapseButton', 'player']

    connect() {
        this.initPlayer();
    }

    initPlayer() {
        let player = $('#youtubePlayer');

        // Code taken straight from https://developers.google.com/youtube/iframe_api_reference?hl=pl
        player = new YT.Player(player, {
            videoId: 'Va297erJjJ4',
            events: {
                'onReady': onPlayerReady,
                'onStateChange': onPlayerStateChange
            }
        });

        function onPlayerReady(event) {
            event.target.playVideo();
        }

        var done = false;

        function onPlayerStateChange(event) {
            if (event.data == YT.PlayerState.PLAYING && !done) {
                setTimeout(stopVideo, 6000);
                done = true;
            }
        }

        function stopVideo() {
            player.stopVideo();
        }
    }
}
<div class="youtube-player-modal" data-controller="youtubeplayer">
    <div id="youtubePlayer"></div>
</div>

我这里写了一个解决你的问题的方法,那里你必须先调用ready函数,然后再给回调。 https://codesandbox.io/s/funny-browser-n3m3j?file=/src/Player.js

// loadScript.js
import Player from "./Player";

function loadScript(url, callback) {
  var script = document.createElement("script");
  script.type = "text/javascript";
  if (script.readyState) {
    // only required for IE <9
    script.onreadystatechange = function () {
      if (script.readyState === "loaded" || script.readyState === "complete") {
        script.onreadystatechange = null;
        callback();
      }
    };
  } else {
    //Others
    script.onload = function () {
      callback();
    };
  }

  script.src = url;
  document.getElementsByTagName("head")[0].appendChild(script);
}

loadScript("https://www.youtube.com/iframe_api", new Player().initPlayer);


// Player.js
import { Controller } from "stimulus";

import $ from "jquery";

export default class extends Controller {
  static targets = ["collapseButton", "player"];

  connect() {
    this.initPlayer();
  }

  initPlayer() {
    let player = $("#youtubePlayer");
    window.YT.ready(function () {
      player = new window.YT.Player("youtubePlayer", {
        height: "360",
        width: "640",
        videoId: "Va297erJjJ4",
        events: {
          onReady: onPlayerReady,
          onStateChange: onPlayerStateChange
        }
      });
    });

    function onPlayerReady(event) {
      event.target.playVideo();
    }

    var done = false;

    function onPlayerStateChange(event) {
      if (event.data === window.YT.PlayerState.PLAYING && !done) {
        setTimeout(stopVideo, 6000);
        done = true;
      }
    }

    function stopVideo() {
      player.stopVideo();
    }
  }
}
<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div
      class="youtube-player-modal"
      data-controller="youtubeplayer"
    >
      <div id="youtubePlayer"></div>
    </div>

    <script src="src/loadScript.js"></script>
  </body>
</html>