.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');
内部を知りすぎず、「このモジュールを使用したいので、ここからいくつかのコードを使用します」と言うことができるようにしたいだけです。
これはきれいではありませんが、動作します:
<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の純粋主義者であることは本当に嫌いです。しかし、予想どおりに機能します。これらのいdocument.write()
sを簡単にラップして、それらを見る必要がないようにすることができます。テストを行い、ノードを作成して追加し、document.write()
にフォールバックすることもできます。
これはかなり遅いですが、これをやりたい人に将来参照するために、次を使用できます:
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-loaded /
非同期プログラミングはやや複雑です複雑です要求ステートメントに従う代わりに、関数にカプセル化されます。 ただし、ユーザーエクスペリエンスが可能なリアルタイム動作大幅に改善されました。サーバーやネットワークの停滞は見られないため、ブラウザーはクラッシュしたかのように動作します。 同期プログラミングは無礼および人が使用するアプリケーションでは使用しないでください。
ダグラス・クロックフォード( YUI Blog )
さて、それはでこぼこの乗り物になるからです。 javascriptを使用してスクリプトを動的にロードすることを求める人が増えています。これはホットなトピックのようです。
これが非常に人気になった主な理由は次のとおりです。
modularityについて:クライアント側の依存関係の管理は、クライアント側で処理する必要があることは明らかです。特定のオブジェクト、モジュール、またはライブラリが必要な場合は、それを要求して動的にロードします。
エラー処理:リソースに障害が発生した場合、影響を受けるスクリプトに依存する部分のみをブロックする機会が得られます。少し遅れます。
パフォーマンスはウェブサイト間で競争力のあるエッジになり、検索ランキング要素になりました。動的スクリプトでできることは、ブラウザーがスクリプトを処理する方法のデフォルトのブロック方法とは対照的に、非同期動作を模倣することです。 Scripts block他のリソース、scripts blockHTMLドキュメントのさらなる解析、 スクリプトブロックUI。ダイナミックスクリプトタグとそのクロスブラウザの代替により、実際の非同期リクエストを実行し、依存コードが利用可能な場合にのみ依存コードを実行できます。スクリプトは他のリソースと並行してロードされ、レンダリングは完璧になります。
一部の人々が同期スクリプトにこだわる理由は、同期スクリプトに慣れているためです。彼らはそれがデフォルトの方法であると考え、それはより簡単な方法であり、一部の人はそれが唯一の方法だとさえ考えるかもしれません。
ただし、アプリケーションの設計に関してこれを決定する必要がある場合に注意する必要があるのは、エンドユーザーエクスペリエンスだけです。そして、この分野では非同期を打ち負かすことはできません。ユーザーは即時の応答(またはpromiseと言います)を取得し、promiseは何もないよりも常に優れています。空白の画面は人々を怖がらせます。開発者は、パフォーマンスの向上のパフォーマンスを怠ることはできません。
そして最後に、汚い面についてのいくつかの言葉。ブラウザー間で機能させるために行うべきこと:
上記の答えは、正しい方向を示してくれました。ここに私が働いて得たものの一般的なバージョンがあります:
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
}
これは、動的なスクリプト読み込みのまともな概要のように見えます。 http://unixpapa.com/js/dyna.html
この質問に対する既存の回答(および他のスタックオーバーフロースレッドでのこの質問のバリエーション)には、次の問題がありました。
または、少し正確に:
戻る前にスクリプトをロードし、デバッガーですべてのスクリプトに適切にアクセスできる最終的なソリューション(少なくとも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);
}
};
function include(file){
return new Promise(function(resolve, reject){
var script = document.createElement('script');
script.src = file;
script.type ='text/javascript';
script.defer = true;
document.getElementsByTagName('head').item(0).appendChild(script);
script.onload = function(){
resolve()
}
script.onerror = function(){
reject()
}
})
/*I HAVE MODIFIED THIS TO BE PROMISE-BASED
HOW TO USE THIS FUNCTION
include('js/somefile.js').then(function(){
console.log('loaded');
},function(){
console.log('not loaded');
})
*/
}
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'
]) ;
皮肉なことに、私はあなたが欲しいものを持っていますが、あなたが持っていたものにより近いものが欲しいです。
私は物事を動的および非同期に読み込みますが、load
コールバックを使用します(dojoとxmlhtpprequestを使用)
dojo.xhrGet({
url: 'getCode.php',
handleAs: "javascript",
content : {
module : 'my.js'
},
load: function() {
myFunc1('blarg');
},
error: function(errorMessage) {
console.error(errorMessage);
}
});
より詳細な説明については、 here を参照してください
問題は、コードが回避される行のどこかであり、コードに問題がある場合、console.error(errorMessage);
ステートメントは実際のエラーではなくeval()
がある行を示します。これは非常に大きな問題であり、実際に<script>
ステートメントに変換しようとしています( here を参照)。