13

JS ファイルを指すスクリプト タグを動的に追加する typescript アプリケーションがあります。いくつかの制限により、これらのスクリプト タグを html ファイルで静的に定義することはできないため、次のように typescript を介して動的に追加します。

for (let src of jsFiles) {
  let sTag = document.createElement('script');
  sTag.type = 'text/javascript';
  sTag.src = src;
  sTag.defer = true;
  document.body.appendChild(script);
}

ここで、スクリプト タグを動的に追加すると、読み込まれる順序が保証されないように見えることに気付きました。残念ながら、jsFiles配列には相互に依存するスクリプトがあります。そのため、配列内の 2 番目のスクリプトは、最初のスクリプトが完全にロードされた後にのみロードできます。2 番目のスクリプトは、最初のスクリプトで定義された関数を参照します。スクリプトを動的に追加するときに、スクリプトを順序付けて実行する順序を指定する方法はありますか (html ファイルでスクリプト タグを静的に定義するときに順序付けを行う方法と同様です)。

PS アプリケーションのパフォーマンスが低下していることに気付いたので、この問題を解決するために onload コールバックを使用することは避けたいと思います。私の 2 番目のスクリプト ファイルは非常に大きく、それが劣化の原因であると推測しています。

4

3 に答える 3

18

その要件を克服するためのいくつかの代替案について言及できます。

  1. ライブラリを使用して依存関係を注入します (AMD または CommonJS モジュール)
    モジュールを使用するだけです。ES2015: import/ export、または CommonJS: require().
  2. scriptプログラムでタグを作成しonload、スクリプトが非同期でロードされたときに反応するようにコールバックを設定します。この属性async = trueはデフォルトで設定されています。
  3. 挿入するスクリプトを変更することが許可されている場合は、スクリプトの最後にobjectorを含む行を追加しarrayて、既にロードされているスクリプトを追跡します。
  4. スクリプトをテキスト ( ) として取得し、必要な順序でスクリプトを使用して をXMLHttpRequest構築し、最後に、次の方法でテキスト スクリプトを実行できます。stringeval()
  5. あまりお勧めしませんが頻繁に使用されるオプションでsetInterval、スクリプトが既に実行されているかどうかを確認するように設定します。

最初のオプションを選択することをお勧めします。しかし、学術的な目的のために、2 番目のオプションを説明します。

scriptプログラムでタグを作成しonload、スクリプトが非同期でロードされたときに反応するようにコールバックを設定します。

スクリプトローダーについて読むことをお勧めします。

次の例は、スクリプト インジェクションを管理するための小さなモジュールであり、これがその背後にある基本的な考え方です。

let _scriptsToLoad = [
  'path/to/script1.js',
  'path/to/script2.js',
  'path/to/script3.js'
];

function createScriptElement() {
  // gets the first script in the list
  let script = _scriptsToLoad.shift();
  // all scripts were loaded
  if (!script) return;
  let js = document.createElement('script');
  js.type = 'text/javascript';
  js.src = script;
  js.onload = onScriptLoaded;
  let s = document.getElementsByTagName('script')[0];
  s.parentNode.insertBefore(js, s);
}

function onScriptLoaded(event) {
  // loads the next script
  createScriptElement();
};

このプランカーでは、スクリプトのインジェクションを特定の順序で非同期にテストできます。

主なアイデアは、次のメソッドを公開することにより、スクリプトと対話して注入できるようにする API を作成することでした。

  • addScript: ロードする各スクリプトの URL または URL のリストを受け取ります。
  • load: タスクを実行して、指定された順序でスクリプトをロードします。
  • reset: スクリプトの配列をクリアするか、スクリプトのロードをキャンセルします。
  • afterLoad: すべてのスクリプトがロードされた後に実行されるコールバック。
  • onComplete: すべてのスクリプトがロードされた後に実行されるコールバック。

私はFluent Interfaceメソッド チェーンテクニックが好きなので、そのようにモジュールを作成しました。

  scriptsLoader
    .reset()
    .addScript("script1.js")
    .addScript(["script2.js", "script3.js"])
    .afterLoad((src) => console.warn("> loaded from jsuLoader:", src))
    .onComplete(() => console.info("* ALL SCRIPTS LOADED *"))
    .load();

上記のコードでは、最初に"script1.js"ファイルをロードしてafterLoad()コールバックを実行し、次に同じことを"script2.js"行い"script3.js"、すべてのスクリプトがロードされた後にonComplete()コールバックが実行されます。

于 2016-08-09T01:33:56.050 に答える
1

アプリケーションのパフォーマンスが低下していることに気付いたので、この問題を解決するために onload コールバックを使用することは避けたいと思います。

ファイルを順序付けしたい場合...各ファイルのオンロードを待つ必要があります。それを回避する方法はありません。

これは私が一度書いたユーティリティ関数です:

/**
 * Utility : Resolves when a script has been loaded
 */
function include(url: string) {
  return new Promise<{ script: HTMLScriptElement }>((resolve, reject) => {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    script.onload = function() {
      resolve({ script });
    };

    document.getElementsByTagName('head')[0].appendChild(script);
  });
}
于 2016-08-09T03:52:04.907 に答える
0

jQuery を使用している人のために、指定された順序ですべてのスクリプトをロードするように @adeneo スクリプトを改善しました。チェーンの読み込みを行わないので非常に高速ですが、さらに高速にしたい場合は、50 ミリ秒の待機時間を変更してください。

$.getMultiScripts = function(arr, path) {

    function executeInOrder(scr, code, resolve) {
        // if its the first script that should be executed
        if (scr == arr[0]) {
            arr.shift();
            eval(code);
            resolve();
            console.log('executed', scr);
        } else {
            // waiting
            setTimeout(function(){
                executeInOrder(scr, code, resolve);
            }, 50);
        }
    }

    var _arr = $.map(arr, function(scr) {

        return new Promise((resolve) => {
            jQuery.ajax({
                type: "GET",
                url: (path || '') + scr,
                dataType: "text",
                success: function(code) {
                    console.log('loaded  ', scr);
                    executeInOrder(scr, code, resolve);
                },
                cache: true
            });
        });

    });
        
    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));
        
    return $.when.apply($, _arr);
}

使い方:

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});
于 2020-11-05T15:43:27.223 に答える