javascriptモジュールのロードについての思索2
6496 ワード
何日か考えてみたら、「書類とモジュール」という問題を思い出しました.私たちのモジュールは確かにJSファイルに書いています.これらのモジュールはコアモジュールと周辺モジュールに分けられます.コアモジュールはもちろんメインファイルに書かれています.最も重要な論理、キャリア、列、名前空間ビルダーなどが含まれています.しかし、一つのファイルが一つのモジュールだけに存在すると、これはあまりにも無駄であり、請求法が多すぎるため、複数のモジュールが一つのファイルに「共生」する場合があります.メインファイルにあるコア以外のモジュールを内囲モジュールと呼びます.他の内囲と外郭はあまり区別がないです.ただ所在書類が違っています.便利さのために内部モジュールは外周モジュールに依存しないでください.
しかし、私たちはScriptタグでJSファイルを引用すると、中のスクリプトをがやがやと実行します.最も主要なロジックは気にせずに解析できます.しかし、内囲モジュールに対しては、論理は関数体に置いてあり、制御流はそれらの上をかすめていくだけで、中のものには触れられない.このモジュール名とコールバック関数との関連付けられた構成は、処理関数(以下、useという)に入り、処理列に追加されます.依存性がある場合は、モジュールに依存するファイルがロードされているかどうかを検出し、ファイルがロードされていない場合は、このモジュールはフレームの名前空間に組み込まれていることを検出し、最後にコールバック関数を実行します.
上の分析から分かるように、この中の操作は大体においていくつかの種類に分けられます.ファイルのロード、モジュールの組み立てと実行のコールバックは順番にしか実行できません.多くの種類のライブラリのフレームワークを総合して、提供される解決策はこの2つです.動的script挿入とAjaxフィードバック解析です.ダイナミックscript挿入とは、scriptノードを生成し、ターゲットsrcを設定し、headノードに挿入することである.document.writeを使わない理由は、bodyに挿入されたものであり、多くの欠陥があります.具体的にはこの文章を参照してください. Ajaxフィードバック解析は、XMLHttpオブジェクトを利用して、戻ってきたレスポンスTextを再度グローバル解析することである.グローバル解析を行うには、Windows.eval(標準ブラウザ)またはwindow.execScript(IE)、またはもう一つのscriptタグを使って解析を行う必要があります.この方法は多くの互換性のある問題に対処する必要があることが分かります.また、ドメインをまたぐ問題にも…. 私の立場は明らかになりました.第一種類を使います.しかし、scriptタグはコールバックの処理について多くの問題があります.
ドジョウであれ、JSANであれ、またはYUIであれ、ましてやusings.js、require.js、packages.jsなどの小众のライブラリであれ、モジュール名(パッケージ名)をurlに変换する仕组みがある.例えば:
しかし、私たちはScriptタグでJSファイルを引用すると、中のスクリプトをがやがやと実行します.最も主要なロジックは気にせずに解析できます.しかし、内囲モジュールに対しては、論理は関数体に置いてあり、制御流はそれらの上をかすめていくだけで、中のものには触れられない.このモジュール名とコールバック関数との関連付けられた構成は、処理関数(以下、useという)に入り、処理列に追加されます.依存性がある場合は、モジュールに依存するファイルがロードされているかどうかを検出し、ファイルがロードされていない場合は、このモジュールはフレームの名前空間に組み込まれていることを検出し、最後にコールバック関数を実行します.
上の分析から分かるように、この中の操作は大体においていくつかの種類に分けられます.ファイルのロード、モジュールの組み立てと実行のコールバックは順番にしか実行できません.多くの種類のライブラリのフレームワークを総合して、提供される解決策はこの2つです.動的script挿入とAjaxフィードバック解析です.
var script = dom.genScriptNode();
script.src = url
dom.head().appendChild(script);
script.onload = script.onreadystatechange = function(){
if ((!this.readyState) || this.readyState == "loaded" || this.readyState == "complete" ){
if(!dom.done[name]){
alert(" 1")
dom.head().removeChild(script)
}
callback();
}
}
script.onerror = function(){
script.onload = script.onerror = undefined;
alert(" 2")
dom.head().removeChild(script)
}
私達のscriptタグが参照しているJSファイルが存在しない場合、いくつかの標準ブラウザの下で、そのoneerrorイベントがトリガされますが、IEの下でOloadイベントとoneerrorイベントがないため、ロードされたとは判定できません.目標ファイルを成功すれば、dom.done.moduleNameはtrueです.失敗すれば、当然undefinedです.dom.done[name]はtrueで、この不要なscriptタグを削除します.この方法は完璧なはずです.IEと標準ブラウザの互換性があります.残念ながら標準ブラウザは石の塊ではありません.まだ違いがあります.憎らしいoperaはロード失敗の時に致命的なエラーを投げます.これはtry catchさえも力になりません.このurlは絶対的に正確でなければなりません.そのためにはリアルなurlメカニズムを導入します.ドジョウであれ、JSANであれ、またはYUIであれ、ましてやusings.js、require.js、packages.jsなどの小众のライブラリであれ、モジュール名(パッケージ名)をurlに変换する仕组みがある.例えば:
"query"====>"http://localhost:3000/javascripts/dom/query.js"
http://localhost:3000/javascripts/私はbasePathと申します.コアモジュールがあるJSファイルのパスです.domは強制的に追加されました.すべての周辺モジュールファイルはここにある必要があります.JSファイルパスを取る方法は、私のこのブログを参照することができる.極端な場合には、このゲームのルールを放棄しなければならない場合があります.フレームワークは正しいurlを見つけることができません.この時、私達は明示的にそのパスを指摘します.方法はモジュール名に小さい括弧を追加することです.中はその真実のurlです.
var module = "dom."+item,url;
// dom.node(http://www.cnblogs.com/rubylouvre/dom/node.js)
var _u = module.match(/\(([^)]+)\)/);
url = _u && _u[1] ? _u[1] : dom.getBasePath()+"/"+ module.replace(/\./g, "/") + ".js";
var script = dom.genScriptNode();
script.src = url
dom.head().appendChild(script);
var scope = dom.namespace(module,true)
//..........
モジュールとコールバック関数は、私の考えでは同じ白地から出てきたので、それらは同じ方法のコールバック関数です.私はこの方法をアメリカと名づけましたが、YUI 3のaddとuseの職責を兼職しています.例えば、これは外周モジュールqueryです.
// /dom/query.js
dom.use("query",function(){
arguments.callee._attached = true;
dom.query = function(selector,context){
context = context || document
try{
var els = context.querySelectorAll(selector);
return dom.filter(els,function(el){
return el.nodeType === 1
})
}catch(e){
alert(" querySelectorAll")
}
}
},{
use:["collection"]
});
これは他の外周モジュールのコレクションに依存しています.
// /dom/collection.js
dom.use("collection",function(){
arguments.callee._attached = true;
dom.filter = function(array, fn, scope){
var result = [],ri = 0;
for (var i = 0,n = array.length; i < n; i++){
if(fn.call(scope || array[i],array[i],i,array)){
result[ ri++] = array[i];
}
}
return result;
}
dom.each = function(){/**/}
dom.map = function(){/**/}
dom.keys = function(){/**/}
//.....
})
ウェブページでこのように呼び出します.
dom.ready(function(){
dom.use("query", function(){
var els =dom.query("p")
alert(els)
});
});
どのように両者を区別するかは、コールバック関数が尽きることなく呼び出されるため、モジュールはできません.そうでなければ、重要な配置を修正しました.一回しか実行できません.私たちはモジュールであることを識別するものを使う必要があります.次は私が思いつく方法です.
dom.use("collection",function(){
arguments.callee._attached = true;
dom.filter = function(){/**/}
dom.each = function(){/**/}
dom.map = function(){/**/}
dom.keys = function(){/**/}
//.....
})
この関数が一回実行されると静的な属性があります.次にまた列に現れたら、それを検出してスキップします.
if(!fn._attached){//
fn();
}
ファイルについても、このJSファイルが既に読み込まれているなら、私たちはこれ以上ロードしなくてもいいです.だから、私たちはhashを使ってこのメッセージを保存することができます.
dom.loaded.collection = true;
dom.use("collection",function(){
arguments.callee._attached = true;
dom.filter = function(){/**/}
dom.each = function(){/**/}
dom.map = function(){/**/}
dom.keys = function(){/**/}
//.....
})
基本的にはそうです.最後にいくつかの概念を振り返ってみましょう.コアモジュール、フレームの重要な構成部分はもちろんuse関数にはありません.逆に、use関数、整列処理、特徴検出などの重要なものは全部その構成部分です.コアモジュールと同じJSファイルに位置しています.周辺モジュールに依存してはいけません.外周モジュールは他の外周モジュールに依存することができます.それは必ずコアモジュールと内周モジュールのもので構成されています.これらのものは外周負荷時にすでに存在していますので、これらの内部依存性を書き出す必要がありません.それらの周辺モジュールをリストするだけでいいです.ファイルがロードされているかどうかは未知数です.列を処理するのは普通の配列です.中の要素はモジュール名、モジュール自体とコールバック関数です.終わります.