ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript フレームワーク設計の読書ノート モジュールの読み込み system_javascript スキル

JavaScript フレームワーク設計の読書ノート モジュールの読み込み system_javascript スキル

WBOY
WBOYオリジナル
2016-05-16 16:29:231201ブラウズ

モジュールの読み込みとは、開発とメンテナンスを容易にするために、実際には js を多くのモジュールに分割することです。したがって、多くの js モジュールをロードする場合、ユーザー エクスペリエンスを向上させるために動的にロードする必要があります。

モジュールローディングライブラリを紹介する前に、まずメソッドを紹介します。

動的読み込み js メソッド:

コードをコピーします コードは次のとおりです:

functionloadJs(url, callback){
var node = document.createElement("script");
ノード[window.addEventListener ? "onload":"onreadystatechange"] = function(){
If(window.addEventListener || /loaded|complete/i.test(node.readyState)){
コールバック();
node.onreadystatechange = null;
 }
node.onerror = function(){};
Node.src = url;
var head = document.getElementsByTagName("head")[0];
head.insertBefore(node,head.firstChild); //IE6 でヘッド ラベルが閉じる前に appendChild を使用するとエラーが発生するのを防ぐために、head の最初のノードの前に挿入します。
}

Situ Zhengmei はモジュールの読み込みを導入するために作成されたマス フレームワークを使用しているため、業界で最も一般的に使用されているのは require.js と sea.js です。だからこそ、個性が強いのだと思います。

sea.js のモジュール読み込みプロセスについて話しましょう:

chaojidan.jsp のページの head タグ内に sea.js を導入すると、seajs オブジェクトが取得されます。

同時にindex.jsも導入します。

index.jsのコードは次のとおりです:

コードをコピーします コードは次のとおりです:
seajs.use(['./a','jquery'],function(a,$){
var num = a.a;
$('#J_A').text(num);
})

a.js:

コードをコピーします コードは次のとおりです:
define(function(require,exports,module){
var b = require('./b');
var a = function(){
return 1 parseInt(b.b());
}
エクスポート.a = a;
})

b.js:

コードをコピーします コードは次のとおりです:
define(function(require,exports,module){
var c = require('./c');
var b = function(){

return 2 parseInt(c.c());
}
エクスポート.b = b;
})

c.js:


コードをコピー コードは次のとおりです:
define(function(require,exports,module){
var c = function(){
3 を返します;
}
エクスポート.c = c;
})

上記のことから、モジュール a は b に依存し、b は c に依存していることがわかります。

プログラムがindex.jsに入ると、seajsはuseメソッドを呼び出します。

コードをコピーします コードは次のとおりです:
seajs.use = function(ids, callback) {
globalModule._use(ids, callback)
}

注: globalModule が seajs 用に初期化されるとき (sea.js が導入されるとき)、モジュールのインスタンスは var globalModule = new Module(util.pageUri, STATUS.COMPILED)
このとき、 ids -> ['./a','jquery'], callback -> function(a,$){var num = a.a;$('#J_A').text(num);}

次に

GlobalModule._use(ids, callback)

が呼び出されます

コードをコピーします コードは次のとおりです:

Module.prototype._use = function(ids, callback) {
var uris =solve(ids, this.uri) //解決['./a','jquery']
This._load(uris, function() { //解析された a、jquery モジュールのアドレス [url1, url2] を計算し、_load メソッドを呼び出します。
//util.map: すべてのデータ メンバーが一度に指定された関数を実行し、元の配列メンバーによって実行されたコールバックの結果である新しい配列を返します
var args = util.map(uris, function(uri) {

return uri ? crashedModules[uri]._compile(): null;// URL が存在する場合は、_compile メソッドを呼び出します。
})
if (callback) { callback.apply(null, args) }
})
}

_load メソッドを呼び出した後、2 つのコールバック関数が表示されるため、function(a,$){var num = a.a;$('#J_A').text(num);} を callback1, this._load(uris, function() { }) コールバック メソッドを
としてマークします。 解決メソッドはモジュールアドレスを解決するため、ここでは詳細を説明しません。
最後に、var uris =solve(ids, this.uri) の URI が ['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery) に解析されます。 /1.7.2/juqery-debug.js']、モジュール パスの解決が完了しました。

そして this._load

が次に実行されます。

コードをコピー コードは次のとおりです:

// _load() メソッドは主に、どのリソース ファイルがまだ準備ができていないのかを判断します。すべてのリソース ファイルが準備完了状態にある場合、callback2
が実行されます。 // この処理では循環依存関係も判定され、アンロードされたjsがロードされます
Module.prototype._load = function(uris, callback2) {
//util.filter: すべてのデータ メンバーが一度に指定された関数を実行し、新しい配列を返すようにします。この配列は、元の配列メンバーがコールバックを実行した後に true を返すメンバーです
//unLoadedUris はコンパイルされていないモジュール URI 配列です
var unLoadedUris = util.filter(uris, function(uri) {
// 実行関数のブール値が true であるメンバーを返します。uri が存在する場合と内部変数 cacheModules に存在しない場合、または格納されている情報のステータス値が STATUS.READY
未満の場合に true を返します。 // STATUS.READY 値は 4 です。値が 4 未満の場合は、取得中またはダウンロード中である可能性があります。
uri && (!cachedModules[uri] ||
を返す) キャッシュされたモジュール[uri].status });
//uris 内のすべてのモジュールの準備ができたら、コールバックを実行して関数本体を終了します (この時点で、モジュールの _compile メソッドが呼び出されます)。
var length = unLoadedUris.length
if (length === 0) { callback2() return }
//まだロードされていないモジュールの数
var 残り = 長さ
// クロージャを作成し、ロードされていないモジュールのロードを試みます
for (var i = 0; i < length; i ) {
(関数(uri) {
//内部変数cachedModulesにuriの格納情報が存在しないかを判定し、Moduleオブジェクトをインスタンス化する
var module = queuedModules[uri] ||
(cachedModules[uri] = 新しいモジュール(uri, STATUS.FETCHING))
//モジュールのステータス値が 2 以上の場合、モジュールはダウンロードされ、ローカルにすでに存在していることを意味します。この時点で、onFetched()
が実行されます。 //それ以外の場合は、fetch(uri, onFetched) を呼び出してリソース ファイルのダウンロードを試行します。リソース ファイルがダウンロードされた後、onload がトリガーされ、onload で onFetched コールバック メソッドが実行されます。
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
関数 onFetched() {
module = cachedModules[uri]
//モジュールのステータス値がSTATUS.SAVED以上の場合、モジュールの依存関係情報がすべて取得されたことを意味します
if (module.status >= STATUS.SAVED) {
//getPureDependency: 循環依存関係を持たない依存関係配列を取得します
var deps = getPureDependency(module)
//依存関係配列が空でない場合
if (deps.length) {
//すべての依存関係がロードされ、コールバックが実行されるまで、_load() メソッドを再度実行します
Module.prototype._load(deps, function() {
cb(モジュール)
})
}
//依存関係配列が空の場合は、cb(module)
を直接実行します else {
cb(モジュール)
}
}
// 404 などで取得が失敗した場合、またはモジュール仕様に準拠していない場合
//この場合、module.status は FETCHING または FETCHED
のままになります。 その他 {
cb()
}
}
})(unLoadedUris[i])
}
// cb メソッド - すべてのモジュールをロードした後に実行されるコールバック
関数 cb(モジュール) {
// モジュールのストレージ情報が存在する場合、そのモジュールのストレージ情報のステータス値を STATUS.READY
に変更します モジュール && (module.status = STATUS.READY)
// すべてのモジュールがロードされた場合にのみコールバックを実行します。
--remain === 0 && callback2()
}
}
}

ここでの unLoadedUris の配列の長さは 2、['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery/1.7.2/juqery- debug .js'] を実行すると、js パスで名前が付けられた 2 つのクロージャーが次に生成されます。

例として http://localhost/test/SEAJS/a.js を取り上げます
次へ: まず、モジュールが作成されます:

コードをコピーします コードは次のとおりです:

queuedModules('http://localhost/test/SEAJS/a.js') = 新しいモジュール('http://localhost/test/SEAJS/a.js',1)
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)

この時点ではモジュール a がロードされていないため、次に fetch(uri, onFetched) 、つまり fetch('http://localhost/test/SEAJS/a.js', onFetched) が実行されます。

コードをコピー コードは次のとおりです:

関数 fetch(uri, onFetched) {
// マップ内のルールに従って、uri を新しいリクエスト アドレスに置き換えます
var requestUri = util.parseMap(uri)
// まず、取得したリストに requestUri レコードがあるか確認します
If (fetchedList[requestUri]) {
// このとき、元のuriのモジュール格納情報をmapで再定義したrequestUriにリフレッシュする
キャッシュされたモジュール[uri] = キャッシュされたモジュール[リクエストUri]
// onFetched を実行して戻ります。これは、モジュールが正常に取得されたことを意味します
onFetched()
戻る
}
//取得リストのrequestUriの格納情報を問い合わせる
If (fetchingList[requestUri]) {
// コールバックリストの URI に対応するコールバックを追加し、
を返します CallbackList[requestUri].push(onFetched) //フェッチ中の場合は、このモジュールの onFetched コールバック メソッドを配列にプッシュして戻ります。
戻る
}
// 取得しようとしているモジュールが fetchedList と fetchingList にない場合は、その情報をリクエスト リストとコールバック リストにそれぞれ追加します
fetchingList[requestUri] = true
callbackList[requestUri] = [onFetched]
// 取得します
Module._fetch(
requestUri、
function() {
fetchedList[requestUri] = true
// モジュールのステータスを更新します
// module.status が STATUS.FECTCHING と等しい場合、モジュールのステータスを FETCHED
に変更します var module = cachedModules[uri]
If (module.status === STATUS.FETCHING) {
module.status = STATUS.FETCHED
}
if (fetchingList[requestUri]) {
fetchingList[requestUri]
を削除します }
// callbackList を呼び出します。コールバックの統合実行
if (callbackList[requestUri]) {
util.forEach(callbackList[requestUri], function(fn) {
Fn () // fn はモジュール A に対応するオンフェッチされたメソッドです。
})
callbackList[requestUri]
を削除します }
}、
config.charset
)
}

次に、Module._fetch() が実行されます。ここでのコールバック関数は callback3 と呼ばれます。

このメソッドは、loadJs メソッドを呼び出して、.js ファイルを動的にダウンロードします。 (aとjqueryがあるので新たにスクリプトが2つ作成されます) ここで質問ですが、新規にスクリプトを作成してheadに追加するとjsファイルがダウンロードされます。ダウンロードされますが、jquery を待ちます。スクリプトは作成されてヘッドに追加された後にのみダウンロードされます (Google デバッガーはブレークポイントを設定し、常に保留中と表示されます)。これはなぜでしょうか?
(ここを読むことをお勧めします:http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff。ここでは追加の問題について説明します。レイアウトに使用するテーブルを減らす必要がある理由がわかるかもしれません。レイアウトするときに複数の計算が必要ですが、div は 1 つだけ必要です。同時に、美的電子商取引の面接担当者は、テーブルを表示する前にテーブルを完全に解析する必要があると言いました。そのため、IE6、7、8 では、innerHTML を使用して「

」を作成すると、<タグが解析された分だけ表示されます。 ;tbody>) が自動的に追加されます。
ダウンロードが成功すると、解析されて実行され、define メソッドが実行されます。モジュール a のコードが最初に実行されます。
define(id,deps,function(){}) メソッド分析

コードをコピーします コードは次のとおりです:

//定義を定義します、id: モジュール ID、deps: モジュールの依存関係、ファクトリー
Module._define = function(id、deps、factory) {
//依存関係を解決する //deps が配列型ではなく、factory が関数の場合
if (!util.isArray(deps) && util.isFunction(factory)) { // 関数本体の require 文字列と定期的に一致し、返す配列を形成して値を deps
に代入します deps = util.parseDependency(factory.toString())
}
//メタ情報を設定する
var meta = { id: id, 依存関係: deps, ファクトリー: ファクトリー }
if (document.attachEvent) {
// 現在のスクリプトのノードを取得します
var script = util.getCurrentScript()
// スクリプトノードが存在する場合
if (スクリプト) {
// 元の URI アドレスを取得
derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script)) }
if (!derivedUri) {
util.log('次のインタラクティブ スクリプトから URI を取得できませんでした:', Factory.toString(), 'warn')
}
}
……
}

define は最初にファクトリに対して判定を実行し、それが関数であるかどうかを判断します (理由は、define にはファイルやオブジェクトも含めることができるためです)

関数の場合、関数はfactory.toString()を通じて取得され、a.jsの依存関係は通常のマッチングを通じて取得され、依存関係はdepsに保存されます

a.js の場合、その依存関係は b.js であるため、deps は ['./b'] になります

そしてa.jsの情報を保存します var meta = { id: id, dependency: deps, Factory: Factory }

a.js の場合、meta = { id : unknown 、依存関係 : ['./b'] 、ファクトリー : function(xxx){xxx}}

IE 6 ~ 9 のブラウザでは、現在実行中の js のパスを取得できますが、標準のブラウザではこれは実現できないため、一時的に anonymousModuleMeta = meta にメタ情報を割り当てます。

次に、onload がトリガーされ、コールバック メソッド callback3 が呼び出されます。このコールバック メソッドは、現在のコールバック モジュール (a.js) のステータス値を変更し、module.status = STATUS.FETCHED に設定します。

次に、コールバックキュー callbackList 内の a.js に対応するコールバック (onFetched) が一律に実行されます。

onFetched メソッドは、モジュール a に依存モジュールがあるかどうかを確認します。 a は b に依存しているため、モジュール a が依存する b.js に対して _load() が実行されます。

は b モジュールをダウンロードし、最初に jquery の定義メソッドを実行します。 jquery はモジュールに依存しないため、onload コールバックの後は。 onFetched は cb メソッドを呼び出します。

aと同じ手順でbを実装すると、cモジュールがダウンロードされます。最後に、モジュール c、b、a がすべてダウンロードされて実行され、onload が完了した後、cb メソッドも呼び出されます (最初に c、次に b、次に c)

すべてのモジュールの準備が完了すると、callback2 メソッドが呼び出されます。
最後に callback2 にコールバックし、a および jquery モジュールの _compile メソッドを実行します:

まず a.js モジュールをコンパイルすると、モジュール a の関数が実行されます。 a には require(b.js) が含まれているため、モジュール b の関数が実行されます。 モジュール a の関数の実行が開始されます
モジュール b の関数の実行が開始されます
モジュール c の関数が実行を開始します
モジュールcの関数が実行される
モジュール b の関数が実行されます
モジュールaの関数が実行される

最後にjquery関数を実行します。

コンパイルが完了すると、callback1 が実行され、a と jquery オブジェクトが使用できるようになります。

追記: seajs のバージョンが更新され、現在 _compile メソッドはありません。 (皆さんもぜひ見に行ってください、私も見に行きたいです)

それでは、seajs のモジュール Compilation_compile 処理について説明します。

最初は a.js のコンパイルです

コードをコピー コードは次のとおりです:

Module.prototype._compile = function() {
126 var モジュール = これ
127 // モジュールがコンパイルされている場合は、直接 module.exports
を返します 128 if (module.status === STATUS.COMPILED) {
129 return module.exports
130 }
133 // 1. モジュール ファイルは 404.
134 // 2. モジュール ファイルが有効なモジュール形式で書き込まれません。
135 // 3. その他のエラーの場合。
136 //ここでは、いくつかの異常な状況を処理し、直接 null を返します
137 if (module.status < STATUS.SAVED && !hasModifiers(module)) {
138 null を返す 139 }
140 // モジュールのステータスを COMPILING に変更し、モジュールがコンパイル中であることを示します
141 モジュール.ステータス = STATUS.COMPILING
142
143 // モジュール内で内部的に使用され、他のモジュール (サブモジュールと呼ばれる) が提供するインターフェースを取得し、同期操作を実行するために使用されるメソッドです
144 関数 require(id) {
145 // id
に従ってモジュールのパスを解析します 146 var uri = 解決(id, module.uri)
147 //モジュール キャッシュからモジュールを取得します (ここでのサブモジュールは実際にはメイン モジュールの依存関係としてダウンロードされていることに注意してください)
148 var child = キャッシュモジュール[uri]
149
150 //uri が無効な場合は null を返すだけです。
151 //子が空の場合、パラメータの入力と URI が間違っていることを意味するだけであり、null が直接返されます
152 if (!child) {
153 null を返す 154 }
155
156 // 循環呼び出しを回避します。
157 //サブモジュールのステータスが STATUS.COMPILING の場合、循環依存関係によるモジュールの繰り返しコンパイルを避けるために、child.exports を直接返します
158 if (child.status === STATUS.COMPILING) {
159 return child.exports
160 }
161 //初期化中に現在のモジュールを呼び出すモジュールを指します。この属性により、モジュール初期化時のコールスタックを取得できます。
162 child.parent = モジュール
163 //コンパイルされた子の module.exports を返します
164 子を返す._compile()
165 }
166 // モジュールを非同期にロードし、ロード完了後に指定されたコールバックを実行するためにモジュールによって内部的に使用されます。
167 require.async = function(ids, callback) {
168 モジュール._use(ids, コールバック)
169 }
170 // モジュール システム内のパス解析メカニズムを使用して、モジュール パスを解析して返します。この関数はモジュールをロードせず、解決された絶対パスのみを返します。
171 require.resolve = function(id) {
172 戻り解決(id, module.uri)
173 }
174 // この属性を通じて、モジュール システムによってロードされたすべてのモジュールを表示できます。
175 // 場合によっては、モジュールを再ロードする必要がある場合、モジュールの URI を取得し、delete require.cache[uri] によってその情報を削除できます。こうすることで、次回使用時に再度取得できるようになります。
176 require.cache = キャッシュされたモジュール
177
178 // require は、他のモジュールが提供するインターフェースを取得するために使用されるメソッドです。
179 モジュール.require = 要求
180 // エクスポートは、モジュール インターフェイスを外部に提供するために使用されるオブジェクトです。
181 module.exports = {}
182 var ファクトリー = module.factory
183
184 // ファクトリが関数の場合、モジュールの構築メソッドを表します。このメソッドを実行すると、モジュールが提供するインターフェースを取得できます。
185 if (util.isFunction(factory)) {
186 コンパイルスタック.push(モジュール)
187 runInModuleContext(ファクトリー、モジュール)
188 コンパイルスタック.ポップ()
189 }
190 // ファクトリがオブジェクトや文字列などの非関数型である場合、モジュールのインターフェイスがオブジェクト、文字列、またはその他の値であることを意味します。
191 // 例:define({ "foo": "bar" });
192 // 例:define('私はテンプレートです。私の名前は {{name}} です。');
193 else if (ファクトリー !== 未定義) {
194 module.exports = 工場
195 }
196
197 // モジュールのステータスを COMPILED に変更し、モジュールがコンパイルされたことを示します
198 モジュール.ステータス = STATUS.COMPILED
199 // seajs.modify()
を通じてモジュールインターフェースの変更を実行します 200 個の execModifiers(モジュール)
201 戻り module.exports
202 }

コードをコピーします コードは次のとおりです:

if (util.isFunction(factory)) {
186 コンパイルスタック.push(モジュール)
187 runInModuleContext(ファクトリー、モジュール)
188 コンパイルスタック.ポップ()
189 }

ここでは module.export を初期化します。 runInModuleContext メソッド:

コードをコピー コードは次のとおりです:

// モジュールコンテキストに従ってモジュールコードを実行します
489 関数 runInModuleContext(fn, module) {
490 // モジュールとモジュール自体に関連する 2 つのパラメータを渡します
491 // エクスポートはインターフェースを公開するために使用されます
492 // require は依存モジュールの取得 (同期) (コンパイル) に使用されます
493 var ret = fn(module.require, module.exports, module)
494 // 次のような戻り値公開インターフェイス フォームをサポートします:
495 // リターン {
496 // fn1 : xx
497 // ,fn2:xx
498 // ...
499 // }
500 if (ret !== 未定義) {
501 module.exports = ret
502 }
503 }

a.js で function メソッドを実行すると、var b = require("b.js"),
が呼び出されます requireメソッドはbのcompileメソッドの戻り値を返しますが、bモジュールにはvar c = require('c.js')があります。
このとき、cのcompileメソッドが呼び出され、cの関数が呼び出されます。cでは、オブジェクトを公開する場合、またはオブジェクトcを返す場合、モジュールc = cのexportsが実行されます。または直接 module.export = c; つまり、最後に module c.export = c が返されるため、モジュール b では変数 c を使用してメソッドとメソッドを呼び出すことができます。モジュール c の c オブジェクトのプロパティ。
同様に、最終的にはモジュール a もモジュール b 内のオブジェクト b のプロパティとメソッドを呼び出すことができます。
どのモジュールであっても、 module.export = xx module が使用されている限り、他のモジュールは require("xx module") を使用して xx モジュール内のさまざまなメソッドを呼び出すことができます。
最終的なモジュールのステータスは module.status = STATUS.COMPILED になります。

コードをコピーします コードは次のとおりです:

Module.prototype._use = function(ids, callback) {
var uris =solve(ids, this.uri) //解決['./a','jquery']
This._load(uris, function() { //解析された a、jquery モジュールのアドレス [url1, url2] を計算し、_load メソッドを呼び出します。
//util.map: すべてのデータ メンバーが一度に指定された関数を実行し、元の配列メンバーによって実行されたコールバックの結果である新しい配列を返します
var args = util.map(uris, function(uri) {

return uri ? crashedModules[uri]._compile(): null;// URL が存在する場合は、_compile メソッドを呼び出します。
})
if (callback) { callback.apply(null, args) }
})
}

この時点では args = [module a.export, module jquery.export];

コードをコピー コードは次のとおりです:

seajs.use(['./a','jquery'],function(a,$){
var num = a.a;
$('#J_A').text(num);
})

このとき、関数内の a と $ はモジュール a.export とモジュール jquery.export になります。

私は現在、jquery ソース コードと jquery フレームワーク設計を勉強しているので、いくつかの経験を共有したいと思います。
インターネットで jquery ソース コードの分析をたくさん読みましたが、もう我慢できませんでした。あまり意味はありませんが、Miaowei Classroom の jquery ソースコード解析をお勧めします。

Situ Zhengmei の JavaScript フレームワーク設計は個人的には難しいですが、注意深く読んでいけば、上級フロントエンド エンジニアになれるでしょう。

Yu Bo の sea.js を学習して使用することをお勧めします。結局のところ、これは中国人自身が作成したものです。当社の新規プロジェクトやリファクタリングはseajsを使用して行われます。

次のステップは、モジュラー ハンドバーと mvc のバックボーンまたは mvvm の angular のソース コードを読むことです。ここで、誰かが私に、どの本を読むべきか、どのウェブサイトを読むべきか、そしてどのビデオを見ればすぐに学べるかについてアドバイスをいただければ幸いです。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。