88

ファイルを同期的に呼び出し、.jsその後すぐに使用することはできますか?

<script type="text/javascript">
    var head = document.getElementsByTagName('head').item(0);
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', 'http://mysite/my.js');
    head.appendChild(script);

    myFunction(); // Fails because it hasn't loaded from my.js yet.

    window.onload = function() {
        // Works most of the time but not all of the time.
        // Especially if my.js injects another script that contains myFunction().
        myFunction();
    };
</script>

これは単純化されています。私の実装では、 createElement のものは関数にあります。制御を返す前に、特定の変数がインスタンス化されているかどうかを確認できる関数を追加することを考えました。しかし、私が制御できない別のサイトからjsを含めるときに何をすべきかという問題がまだあります.

考え?

編集:

何が起こっているのかについての良い説明を与えるので、私は今のところ最良の答えを受け入れました。しかし、これを改善する方法について何か提案があれば、私は喜んで受け入れます。これが私がやりたいことの例です。

// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');

myFunc1('blarg');
myFunc2('bleet');

内部構造を知りすぎないようにして、「このモジュールを使用したいので、そのモジュールのコードを使用します」とだけ言えるようにしたいだけです。

4

12 に答える 12

142

<script>「onload」ハンドラーを使用して要素を作成できます。これは、スクリプトが読み込まれ、ブラウザーによって評価されたときに呼び出されます。

var script = document.createElement('script');
script.onload = function() {
  alert("Script loaded and ready");
};
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);

同期的に行うことはできません。

編集<script>— フォームに忠実に、IE はロード/評価中のタグで「ロード」イベントを発生させないことが指摘されています。したがって、次にすべきことは、XMLHttpRequest を使用してスクリプトを取得し、eval()それを自分で取得することだと思います。(または、追加したタグにテキストを詰め込むと思い<script>ます。の実行環境はeval()ローカルスコープの影響を受けるため、必ずしもやりたいことを実行するとは限りません。)

編集2013 年の初めの時点で、 Requirejsのようなより堅牢なスクリプト読み込みツールを検討することを強くお勧めします。心配すべき特別なケースがたくさんあります。非常に単純な状況では、 yepnopeがあり、これは現在Modernizrに組み込まれています。

于 2010-07-14T16:50:25.930 に答える
26

これはきれいではありませんが、機能します:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

または

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  window.onload = function() {
    functionFromOther();
  };
</script>

スクリプトは、別の<script>タグまたは の前に含める必要がありますwindow.onload()

これは機能しません:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  functionFromOther(); // Error
</script>

Pointy が行ったように、ノードの作成でも同じことができますが、FF でのみ可能です。スクリプトが他のブラウザーでいつ使用できるようになるかは保証されません。

XML 純粋主義者として、私はこれが本当に嫌いです。しかし、それは予想通りに機能します。これらの醜い s を簡単にラップできるdocument.write()ので、それらを見る必要はありません。テストを実行してノードを作成し、それを追加してからフォールバックすることもできますdocument.write()

于 2010-07-20T17:42:44.487 に答える
20

これはかなり遅れていますが、これを行いたい人への将来の参照のために、次を使用できます。

function require(file,callback){
    var head=document.getElementsByTagName("head")[0];
    var script=document.createElement('script');
    script.src=file;
    script.type='text/javascript';
    //real browsers
    script.onload=callback;
    //Internet explorer
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback();
        }
    }
    head.appendChild(script);
}

少し前に短いブログ投稿を行いましたhttp://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its -ロード済み/

于 2013-01-05T16:50:55.283 に答える
7

上記の答えは私を正しい方向に向けました。これが私が働いたものの一般的なバージョンです:

  var script = document.createElement('script');
  script.src = 'http://' + location.hostname + '/module';
  script.addEventListener('load', postLoadFunction);
  document.head.appendChild(script);

  function postLoadFunction() {
     // add module dependent code here
  }      
于 2016-03-16T02:41:00.923 に答える
6

非同期プログラミングは、要求文に従うのではなく、要求を行った結果が関数にカプセル化されるため、少し複雑になります。しかし、サーバーやネットワークの遅延によってブラウザがクラッシュしたかのように動作することがなくなるため、ユーザーが体験するリアルタイムの動作は大幅に改善されます。同期プログラミングは無礼 であり、人が使用するアプリケーションでは使用しないでください。

ダグラス・クロックフォード ( YUI ブログ)

よし、でこぼこ道になるから、シートを締めろ。JavaScript を介してスクリプトを動的にロードすることについて質問する人がますます増えており、話題になっているようです。

これが非常に人気になった主な理由は次のとおりです。

  • クライアント側のモジュール性
  • より簡単な依存関係管理
  • エラー処理
  • パフォーマンス上の利点

モジュール性について: クライアント側の依存関係の管理は、クライアント側で正しく処理する必要があることは明らかです。特定のオブジェクト、モジュール、またはライブラリが必要な場合は、それを要求して動的にロードします。

エラー処理: リソースに障害が発生した場合でも、影響を受けるスクリプトに依存する部分のみをブロックするか、少し遅れて再試行する可能性があります。

パフォーマンスは Web サイト間の競争力となり、現在では検索ランキングの要因となっています。動的スクリプトができることは、ブラウザーがスクリプトを処理する方法のデフォルトのブロック方法とは対照的に、非同期動作を模倣することです。スクリプトは他のリソースをブロックし、スクリプトは HTML ドキュメントのさらなる解析をブロックし、スクリプトは UIをブロックします。動的なスクリプト タグとそのクロス ブラウザーの代替手段を使用すると、実際の非同期要求を実行し、依存コードが利用可能な場合にのみ実行できます。スクリプトは他のリソースと並行して読み込まれ、レンダリングは完璧になります。

一部の人々が同期スクリプトに固執する理由は、彼らがそれに慣れているからです。彼らはそれがデフォルトの方法であり、より簡単な方法であると考えており、それが唯一の方法であると考えている人もいます.

しかし、アプリケーションの設計に関してこれを決定する必要がある場合に考慮すべき唯一のことは、エンド ユーザー エクスペリエンスです。そして、この分野では非同期に勝るものはありません。ユーザーはすぐに応答(または約束を言う) を取得し、約束は常に何もないよりはましです。空白の画面は人を怖がらせます。開発者は、認識されるパフォーマンスを向上させるために怠惰であってはなりません。

そして最後に汚い面についてのいくつかの言葉。ブラウザ間で動作させるには、次のことを行う必要があります。

  1. 非同期的に考える方法を学ぶ
  2. コードをモジュール化して整理する
  3. コードを整理して、エラーとエッジ ケースを適切に処理する
  4. 徐々に強化する
  5. 常に適切な量のフィードバックを心がける
于 2010-07-14T22:41:50.707 に答える
4

この質問に対する既存の回答(および他のスタックオーバーフロースレッドでのこの質問のバリエーション)には、次の問題がありました。

  • ロードされたコードはどれもデバッグ可能ではありませんでした
  • 解決策の多くは、完全にブロックするのではなく、ロードがいつ終了したかをコールバックに知らせる必要がありました。つまり、ロードされた (つまり、ロードした) コードをすぐに呼び出すと、実行エラーが発生します。

または、もう少し正確に:

  • ロードされたコードはどれもデバッグ可能ではありませんでした (ソリューションがスクリプト要素を dom に追加した場合に限り、HTML スクリプト タグ ブロックを除き、個々の表示可能なスクリプトとしては決してありません) => ロードする必要があるスクリプトの数を考えると (およびデバッグ)、これは容認できませんでした。
  • 'onreadystatechange' または 'onload' イベントを使用したソリューションはブロックに失敗しました。これは、コードがもともと 'require([filename, 'dojo/domReady']);' を使用して動的スクリプトを同期的にロードしていたため、大きな問題でした。そして私は道場を脱ぎ捨てていました。

戻る前にスクリプトをロードし、デバッガーですべてのスクリプトに適切にアクセスできるようにする私の最終的な解決策は次のとおりです (少なくとも Chrome の場合)。

警告: 次のコードは、おそらく「開発」モードでのみ使用する必要があります。 (「リリース」モードの場合、動的スクリプトのロードなし、または少なくとも eval なしで、事前パッケージ化と縮小化をお勧めします)。

//Code User TODO: you must create and set your own 'noEval' variable

require = function require(inFileName)
{
    var aRequest
        ,aScript
        ,aScriptSource
        ;

    //setup the full relative filename
    inFileName = 
        window.location.protocol + '//'
        + window.location.host + '/'
        + inFileName;

    //synchronously get the code
    aRequest = new XMLHttpRequest();
    aRequest.open('GET', inFileName, false);
    aRequest.send();

    //set the returned script text while adding special comment to auto include in debugger source listing:
    aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';

    if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
    {
        //create a dom element to hold the code
        aScript = document.createElement('script');
        aScript.type = 'text/javascript';

        //set the script tag text, including the debugger id at the end!!
        aScript.text = aScriptSource;

        //append the code to the dom
        document.getElementsByTagName('body')[0].appendChild(aScript);
    }
    else
    {
        eval(aScriptSource);
    }
};
于 2014-02-15T08:29:06.810 に答える
3

これは、動的スクリプトのロードの適切な概要のように見えます: http://unixpapa.com/js/dyna.html

于 2010-07-14T16:38:28.733 に答える
1

私は、相互に依存する Web サイトに複数の .js ファイルを配置することに慣れています。それらをロードし、依存関係が正しい順序で評価されるようにするために、すべてのファイルをロードし、それらがすべて受信されたらそれらをロードする関数を作成しeval()ました。主な欠点は、これが CDN では機能しないことです。そのようなライブラリ (jQuery など) の場合は、それらを静的に含めることをお勧めします。HTML にスクリプト ノードを動的に挿入しても、少なくとも Chrome では、スクリプトが正しい順序で評価されることは保証されないことに注意してください (これが、この関数を作成する主な理由でした)。

function xhrs(reqs) {
  var requests = [] , count = [] , callback ;

  callback = function (r,c,i) {
    return function () {
      if  ( this.readyState == 4 ) {
        if (this.status != 200 ) {
          r[i]['resp']="" ;
        } 
        else {
          r[i]['resp']= this.responseText ;
        }
        c[0] = c[0] - 1 ;
        if ( c[0] == 0 ) {
          for ( var j = 0 ; j < r.length ; j++ ) {
            eval(r[j]['resp']) ;
          }
        }
      }
    }
  } ;
  if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) {
    requests.length = reqs.length ;
  }
  else {
    requests.length = 1 ;
    reqs = [].concat(reqs);
  }
  count[0] = requests.length ;
  for ( var i = 0 ; i < requests.length ; i++ ) {
    requests[i] = {} ;
    requests[i]['xhr'] = new XMLHttpRequest () ;
    requests[i]['xhr'].open('GET', reqs[i]) ;
    requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ;
    requests[i]['xhr'].send(null);
  }
}

配列を作成せずに同じ値を参照する方法がわかりません(カウント用)。それ以外の場合は、一目瞭然だと思います(すべてがロードされると、eval()すべてのファイルが指定された順序で、そうでない場合は応答を保存するだけです)。

使用例:

xhrs( [
       root + '/global.js' ,
       window.location.href + 'config.js' ,
       root + '/js/lib/details.polyfill.min.js',
       root + '/js/scripts/address.js' ,
       root + '/js/scripts/tableofcontents.js' 
]) ;
于 2013-08-29T13:14:26.820 に答える
0

皮肉なことに、私はあなたが欲しいものを持っていますが、あなたが持っていたものに近いものが欲しいのです.

私は物事を動的かつ非同期にロードしていますが、そのloadようなコールバックを使用しています (dojo と xmlhtprequest を使用)

  dojo.xhrGet({
    url: 'getCode.php',
    handleAs: "javascript",
    content : {
    module : 'my.js'
  },
  load: function() {
    myFunc1('blarg');
  },
  error: function(errorMessage) {
    console.error(errorMessage);
  }
});

詳しい説明はこちら

問題は、行のどこかでコードが評価されることです。コードに何か問題がある場合、console.error(errorMessage);ステートメントはeval()実際のエラーではなく、その行を示します。<script>これは非常に大きな問題であり、実際にステートメントに変換しようとしています (こちらを参照してください。

于 2011-10-24T03:01:12.273 に答える