これは seajs ローダーのコア部分です。一部の IE 互換部分はまだ明確ではありません。重要なのは、各モジュールがどのように順序付けられたロードと CMD 仕様に依存しているかを理解することです。
コードは少し長いので、辛抱強く読む必要があります:
/**
* ローダーのコア
*/
;(function(seajs, util, config) {
//モジュールキャッシュ
var cachedModules = {}
//インターフェースの変更Cache
var queuedModifiers = {}
// コンパイルキュー
varcompileStack = []
// モジュールステータス
var STATUS = {
'FETCHING': 1, // module file is fetching now. モジュールをダウンロード中です
'FETCHED': 2, // モジュール ファイルがダウンロードされました
'SAVED': 3, // モジュール情報が保存されました。モジュール情報が保存されました
'READY': 4, // すべての依存関係と自己はコンパイルの準備ができています。
'COMPILING': 5, //モジュールは現在コンパイル中です。 モジュールはコンパイル中です
'COMPILED': 6 // モジュールはコンパイルされており、 module.exports は利用可能です
}
function Module(uri, status) {
this.uri = uri
this.status = status || 0
// this.id は
// this.dependency を保存するときに設定されます
を保存するときに設定されます//
を保存するときに this.factory が設定されます// コンパイル時に this.exports が設定されます
// コンパイル時に this.parent が設定されます
// this.require が設定されますコンパイル時
}
Module.prototype._use = function(ids, callback) {
//配列に変換、統合操作
util.isString(ids) && ( ids = [ids])
// モジュール システム内のパス解決メカニズムを使用して、モジュール パスを解決して返します
var uris =solve(ids, this.uri)
this._load (uris, function() {
// コンパイル前にモジュールに導入されたプリロード ファイルをロードします。
// コンパイル前に、再度 preload を呼び出してモジュールをプリロードします
// seajs.config はいつでも呼び出すことができるため、コード実行中にプリロード モジュールを構成する時間
preload(function() {
// 各モジュールをコンパイルし、各モジュールのエクスポートをパラメータとしてコールバック関数に渡します
var args = util.map(uris , function(uri) {
return uri ?cachedModules[uri]._compile() : null
})
if (callback) {
// null はこのポインタをコールバックにしますfunction window
callback.apply(null, args)
}
})
})
}
// メインモジュールは依存モジュール (サブモジュールと呼ばれる) をロードし、コールバック関数
Module を実行します。prototype._load = function(uris, callback) {
// Uris 配列をフィルタリングします
// ケース 1: モジュールがキャッシュに存在しないため、その uri // ケース 2: モジュールはキャッシュ内に存在しますが、そのステータスは var unLoadedUris = util.filter(uris, function(uri) {
return uri && (!cachedModules[uri] ||
cachedModules[uri].status < STATUS.READY)
})
var length = unLoadedUris.length
// length の場合0 の場合は、依存関係が 0 であるか、ダウンロードされたことを意味します。その後、コールバック コンパイル操作を実行します
if (length === 0) {
callback()
return
}
var remember = length
for ( var i = 0; i < length; i ) {
// クロージャー、onFetched 関数のコンテキストを提供します
(function(uri) {
// モジュール オブジェクトを作成します
var module = cachedModules[uri] ||
(cachedModules[uri] = new Module(uri, STATUS.FETCHING))
//モジュールがダウンロードされている場合、次に onFetched を実行します。そうでない場合は、フェッチ操作 (リクエストモジュール) を実行します。
module.status >= STATUS.FETCHED ? onFetched() : fetch(uri, onFetched)
function onFetched() {
// 非対応の場合は、cachedModules[uri] が変更されます
module = cachedModules[uri]
// モジュールのステータスが SAVED の場合、モジュールの依存関係が決定されたことを意味し、依存モジュールをダウンロードします
if (module.status >= STATUS.SAVED) {
// モジュール情報から依存モジュールのリストを取得し、循環依存関係を処理
var deps = getPureDependency(module)
// If依存関係が存在します。ダウンロードを続行します。
if (deps.length) {
Module.prototype._load(deps, function() {
cb(module)
})
}
/ / それ以外の場合は、cb を直接実行します。
else {
cb(module)
}
}
// 404 または非モジュールなど、正常にフェッチできなかった可能性があります。//このような場合は、cb 関数を直接呼び出してください。
// 404 やモジュール異常 (コードエラー) など、ダウンロードモジュールが成功しない場合は、モジュールのステータスが取得中または取得済みである可能性があります。
// 、モジュールをコンパイルするとき、コールバック関数は直接実行されます。モジュールは null
else {
cb()
}
}
})(unLoadedUris[i]) のみを返します。 ])
}
function cb(module) {
// モジュールのステータスを READY に変更し、remain が 0 の場合、モジュールの依存関係がダウンロードされたことを意味し、コールバック (モジュール || {}).status < STATUS.READY && (module.status = STATUS.READY )
--remain === 0 && callback()
}
}
Module.prototype._compile = function() {
var module = this
// モジュールがコンパイルされている場合は、直接 module.exports を返します
if (module.status == = STATUS.COMPILED) {
return module.exports
}
// 次の場合は null を返します。
// 1. モジュール ファイルが 404 である。
// 2. モジュール ファイルが有効なモジュール形式で書き込まれていない。
// 3. . その他のエラーの場合
// この場合、null が直接返されます。
if (module.status < STATUS.SAVED && !hasModifiers(module)) {
return null
}
// モジュールのステータスを COMPILING に変更し、モジュールがコンパイル中であることを示します
module.status = STATUS.COMPILING
// モジュールによって内部的に使用されます。他のモジュールによって提供される情報を取得するために使用されるメソッド (サブモジュールと呼ばれる) インターフェース、同期操作
function require(id) {
// id に従ってモジュールのパスを解決します
var uri =solve(id, module) .uri)
// モジュール キャッシュからモジュールを取得します (ここでのサブモジュールは実際にはメイン モジュールの依存関係としてダウンロードされていることに注意してください)
var child = cachedModules[uri]
/ / uri が無効な場合は null を返すだけです。
// child が空の場合は、パラメータの入力が正しくなく、uri が正しくないことを意味するだけであり、null が直接返されます。
if (!child) {
return null
}
// 循環呼び出しを回避します。
// サブモジュールのステータスが STATUS.COMPILING の場合、循環によるモジュールの繰り返しコンパイルを避けるために child.exports を直接返します。 dependency
if (child.status === STATUS.COMPILING) {
return child.exports
}
// 初期化中に現在のモジュールを呼び出すモジュールを指します。この属性に従って、モジュールの初期化時に呼び出しスタックを取得できます。
child.parent = module
// コンパイルされた子の module.exports
return child._compile()
}
// モジュールを非同期にロードし、ロード完了後に指定されたコールバックを実行するためにモジュールによって内部的に使用されます。
require.async = function(ids, callback) {
module._use(ids, callback)
}
// モジュール システム内のパス解決メカニズムを使用して、モジュール パスを解析して返します。この関数はモジュールをロードせず、解決された絶対パスのみを返します。
require.resolve = function(id) {
returnsolve(id, module.uri)
}
// この属性を通じて、モジュール システムによってロードされたすべてのモジュールを表示できます。
// 場合によっては、モジュールをリロードする必要がある場合、モジュールの URI を取得し、delete require.cache[uri] によってその情報を削除できます。次回使用時に再度取得されます。
require.cache = cachedModules
// require は、他のモジュールが提供するインターフェースを取得するために使用されるメソッドです。
module.require = require
// エクスポートは、モジュール インターフェイスを外部に提供するために使用されるオブジェクトです。
module.exports = {}
var Factory = module.factory
// Factory が関数の場合、モジュールの構築方法を表します。このメソッドを実行すると、モジュールが提供するインターフェースを取得できます。
if (util.isFunction(factory)) {
compileStack.push(module)
runInModuleContext(factory, module)
compileStack.pop()
}
// ファクトリはオブジェクト、文字列、およびその他の非関数型が使用される場合、モジュールのインターフェイスはオブジェクト、文字列、およびその他の値になります。
// 例:define({ "foo": "bar" });
// 例:define('私はテンプレートです。私の名前は {{name}} です。'); 🎜> else if (factory !== unknown) {
module.exports = Factory
}
// モジュールのステータスを COMPILED に変更し、モジュールがコンパイルされたことを示します
module .status = STATUS.COMPILED
// seajs.modify()
execModifiers(module)
return module.exports
}
モジュールを通じてモジュール インターフェースの変更を実行します。 _define = function(id , deps, Factory) {
var argsLength = argument.length
//
で渡されたパラメーターの数に応じてパラメーターのマッチングを実行します// define(factory)
// パラメータ 1 つ Case:
// id: 未定義
// deps: 未定義 (依存モジュールのリストは後から通常のルールに従って取り出されます)
// ファクトリー: 関数
if (argsLength === 1) {
factory = id
id = unknown
}
// define(id || deps, Factory)
// パラメータが 2 つの場合:
else if (argsLength === 2) {
// デフォルト:define(id, Factory)
// id : '...'
// deps : 未定義
// ファクトリー : function
factory = deps
deps = unknown
// define(deps, Factory)
// 最初のパラメーターが配列の場合: define(deps, Factory) )
// id : 未定義
// deps : [...]
// ファクトリー : function
if (util.isArray(id)) {
deps = id
id = unknown
}
}
// 依存関係を解析します。
// deps が配列でない場合 (つまり、deps が値を指定しない場合)、依存関係を解析します。正規表現による
if (!util.isArray( deps) && util.isFunction(factory)) {
deps = util.parseDependency(factory.toString())
}
/ / メタ情報。オブジェクト
varderivedUri
内の対応するモジュール
var meta = { id: id, dependency: deps, Factory: Factory } に渡されます。// を試みてください。 IE6-9 で匿名モジュールの URI を取得します。
// IE6-9 の場合、対話型スクリプトを通じてモジュールの URI を取得しようとします。
if (document.attachEvent) {
// current script.
// 現在のスクリプトを取得します
var script = util.getCurrentScript()
if (script) {
// 現在のスクリプトの URL を UnpareseMap してキーとの一貫性を保ちますモジュール キャッシュ内
derivedUri = util.unParseMap(util.getScriptAbsoluteSrc(script) )
}
if (!derivedUri) {
util.log('インタラクティブから URI を導出できませんでしたscript for:',
factory.toString(), 'warn')
// 注: 上記の ID 導出メソッドが失敗した場合は、フォールバック
// onload イベントを使用します
}
}
// 特定のモジュールの URI を直接取得します。
// ID が指定されている場合、パスは ID に従って解析されます。 >// ID が指定されていない場合は明らかに次のようになります:
// IE 以外のブラウザの場合は、未定義を返します (derivedUri が空です)
// IE ブラウザの場合、CurrentScript の src が返されます
// If id を指定します:
// seajs によって解析されたパス URL が返されます
varsolvedUri = id ?solve(id) :derivedUri
// uri が存在する場合、モジュール情報を格納します
if (resolvedUri) {
// IE の場合:
// パッケージ内の最初のモジュールが cachedModules[derivedUri]
// 自身でない場合、見つかったときに正しいモジュールに割り当てる必要があります
if ( solvedUri ===derivedUri) {
var refModule = crashedModules[derivedUri]
if (refModule && refModule.realUri &&
refModule.status === STATUS.SAVED) {
cachedModules[derivedUri] = null
}
}
//ストレージ モジュール情報
var module = save(resolvedUri, meta)
// IE の場合:
// の最初のモジュールを割り当てます
if (derivedUri) {
/ / コンボの場合、cachedModules[derivedUri] は未定義になる可能性があります。
if ((cachedModules[derivedUri] || {}).status === STATUS.FETCHING) {
cachedModules[derivedUri] = module
module .realUri =derivedUri
}
}
else {
// 最初のモジュールを firstModuleInPackage に格納します
firstModuleInPackage || (firstModuleInPackage = module)
}
}
// uri が存在しない場合、モジュール情報はクロージャがある onload コールバックに格納されます。
else {
//作業を「メモ化」するための情報をonloadイベントに保存します。
/ / このときのuriは不明のため、メタ情報はanonymousModuleMetaに一時的に保存され、onloadコールバック内でモジュールの保存操作が行われます。 anonymousModuleMeta = meta
}
}
// コンパイル中のモジュールを取得します
Module._getCompilingModule = function() {
return COMPileStack[compileStack.length - 1]
}
// seajs.cache から合計をすばやく表示します ロードされたモジュール インターフェイスを取得します。戻り値は module.exports 配列です
// セレクターは文字列と正規表現をサポートします
Module ._find = function(selector) {
varmatches = []
util.forEach(util.keys(cachedModules), function(uri) {
if (util.isString(selector) && uri.indexOf(selector) > -1 ||
util.isRegExp( selector) && selector.test(uri)) {
var module = cachedModules[uri]
module.exports &&matches.push (module.exports)
}
})
一致するものを返します
}
// モジュールインターフェイスを変更します
Module._modify = function(id, modifier) {
var uri =solve(id)
var module = cachedModules[uri]
// モジュールが存在し、COMPILED 状態にある場合は、インターフェイス変更操作を実行します。
if (module && module.status === STATUS.COMPILED) {
runInModuleContext(modifier, module)
}
// それ以外の場合は、変更されたインターフェースのキャッシュに入れます
else {
cachedModifiers[uri] || (cachedModifiers[uri] = [])
cachedModifiers[uri].push(modifier)
}
return seajs
}
// プラグイン開発者向け
Module.STATUS = STATUS
Module._resolve = util.id2Uri
Module._fetch = util.fetch
Module.cache = cachedModules
// ヘルパー
// -------
// 存在するモジュールのリストダウンロードされた
var fetchingList = {}
//ダウンロードされたモジュールのリスト
var fetchedList = {}
// コールバック関数のリスト
var callbackList = {}
//匿名モジュールのメタ情報
var anonymousModuleMeta = null
var firstModuleInPackage = null
// 循環依存関係スタック
varcircularCheckStack = []
// バッチ解析されたモジュールへのパス
functionsolve(ids) , refUri) {
if (util.isString(ids)) {
return Module._resolve(ids, refUri)
}
return util.map(ids, function(id) {
returnsolve( id, refUri)
})
}
function fetch(uri, callback) {
// 取得するときは、まずマップ ルールに従って URI を変換します
var requestUri = util.parseMap(uri)
// fethedList (ダウンロードされたモジュール リスト) を検索し、存在する場合は直接戻ってコールバック関数を実行します
// TODO: このモジュールが fetchedList に存在する理由このステップでは?
if (fetchedList[requestUri]) {
// test/issues/debug-using-map を参照
cachedModules[uri] = crashedModules[requestUri]
callback()
return
}
// fetchingList (ダウンロードされるモジュールのリスト) を検索します。存在する場合は、コールバック関数をリストに追加して直接返します。
if (fetchingList[requestUri]) {
callbackList[requestUri].push(callback)
return
}
// このステップに到達すると、モジュールが初めてリクエストされたことを意味します。
// 次に、モジュールを挿入します。 fetchingList 情報に、モジュールが既にダウンロード リストに含まれていることを示し、モジュールに対応するコールバック関数リストを初期化します。
fetchingList[requestUri] = true
callbackList[requestUri] = [callback]
// 取得します
// モジュールを取得します、つまりリクエストを開始します
Module._fetch(
requestUri,
function() {
// モジュールの挿入モジュールがダウンロードされたことを表す fetchedList 内の情報
fetchedList[requestUri] = true
// モジュールのステータスを更新します
var module = cachedModules[uri]
// この時点で、ステータスは STATUS.SAVED である可能性があります。以前は _define で言われていました
if (module.status === STATUS.FETCHING) {
module.status = STATUS.FETCHED
}
// 匿名モジュールのメタデータを保存します
// 匿名モジュールなので (このときクロージャで URI を取得し、ここにモジュール情報が格納されます)
// そして anonymousModuleMeta を空に設定します
if (anonymousModuleMeta) {
save(uri, anonymousModuleMeta)
anonymousModuleMeta = null
}
// パッケージ内の最初のモジュールを cachedModules[uri]
//参照: test/issues/un-correspondence
if ( firstModuleInPackage && module.status === STATUS.FETCHED) {
cachedModules[uri] = firstModuleInPackage
firstModuleInPackage.realUri = uri
}
firstModuleInPackage = null
// Clears
// モジュールがフェッチされて保存されているため、fetchingList 内のモジュール情報をクリアします
if (fetchingList[requestUri]) {
delete fetchingList[requestUri]
}
// callbackList を呼び出します
// コールバック関数を順番に呼び出し、コールバック関数リストをクリアします
if (callbackList[requestUri]) {
util.forEach(callbackList) [requestUri], function(fn) {
fn()
})
delete callbackList[requestUri]
}
},
config.charset
)
}
function save(uri, meta) {
var module = queuedModules[uri] || (cachedModules[uri] = new Module(uri))
// すでに保存されているモジュール
// これを上書きしないでください。 ステータスには 2 つの状態があります:
// STATUS.FETCHING、定義で呼び出され (ID 指定)、モジュール情報を保存します
// STATUS。 onload コールバック関数で呼び出される FETCHED は、モジュール情報を保存します。
if (module.status < STATUS.SAVED) {
// 匿名モジュール ID をその URI と等しくします
// 匿名モジュール (つまり、 、id が指定されていない場合)、その uri を id として使用します
module.id = meta.id || uri
// 依存関係 (配列) から絶対パスを解析し、モジュール情報に格納します
module.dependency =solve(
util.filter(meta .dependency || [], function(dep) {
return !!dep
}), uri)
// ストア ファクトリ (module)実行されるコード (オブジェクトや文字列などの場合もあります)
module.factory = meta.factory
// モジュールのステータスを更新します
// 更新モジュールのステータスは SAVED です。 (現時点では依存関係のみがあり、まだすべてがダウンロードされていない (つまり、まだ準備ができていない) ことに注意してください)
module.status = STATUS.SAVED
}
return module
}
// モジュールコンテキストに従ってモジュールコードを実行します
function runInModuleContext (fn, module) {
// モジュールとモジュール自体に関連する 2 つのパラメータを渡します
// エクスポートはインターフェイスの公開に使用されます
// require は依存モジュールの取得 (同期) (コンパイル) に使用されます
var ret = fn(module.require, module.exports, module)
// 戻り値の公開をサポートしますインターフェース形式:
// return {
// fn1 : xx
// ,fn2 : xx
// ...
// }
if (ret !== 未定義) {
module.exports = ret
}
}
// モジュールにインターフェイス変更があるかどうかを判断します
function hasModifiers(module) {
return !!cachedModifiers [module.realUri || module.uri]
}
// モジュール インターフェイスを変更します
function execModifiers(module) {
var uri = module.realUri || module.uri
var modifiers = cachedModifiers[uri]
// 内部変数cachedModifiersは、seajs.modifyメソッドを通じてユーザーが定義した値を保存するために使用されます 変更ポイント
//modify<によってuriが変更されているかどうかを確認します🎜>if (modifiers) {
// 変更点でファクトリを均一に実行し、変更された module.exports
util.forEach( modifiers, function(modifier) {
runInModuleContext(modifier, module) )
})
// 再度実行されないように、modify メソッドで定義された変更ポイントを削除します
delete queuedModifiers[uri]
}
}
//純粋な依存関係を取得し、循環依存関係を含まない依存関係配列を取得します。
function getPureDependency(module) {
var uri = module.uri
// 依存関係ごとに項目をフィルターし、削除します循環依存関係を形成する可能性のあるものを削除し、警告ログを出力します。
return util.filter(module.dependency, function(dep) {
// まず、チェックされたモジュールの URI を循環依存関係チェック スタックに入れます。 、後続のチェックでは
circularCheckStack = [uri]
を使用します。//次に、モジュール uri がその依存モジュールに対して循環依存関係を持っているかどうかを確認します。
var isCircular = isCircularWaiting(cachedModules[dep ])
if (isCircular) {
// 循環型の場合、uri を循環依存関係チェック スタックに入れます
circularCheckStack.push(uri)
// 循環型警告ログを出力します
printCircularLog(circularCheckStack)
}
return !isCircular
})
}
function isCircularWaiting(module) {
// 依存モジュールが存在しない場合は、Return false 、現時点では依存モジュールの依存関係を取得できないため、ここでは判断できません
// または、モジュールのステータス値が保存済みと等しい場合、false も返されます。が保存されている場合、それはモジュールの情報を表します。
// したがって、循環依存関係が形成されていても、メイン モジュールが必要な場合は、正常にコンパイルでき、メイン モジュールのインターフェイスが返されます。どうやらnodejsはunknownを返すようです)
if (!module || module.status !== STATUS.SAVED) {
return false
}
// 上記の状況ではない場合は、依存モジュールの URI を循環依存関係チェック スタックに置き、その後のチェックでは
circularCheckStack.push(module.uri)
// 依存モジュールの依存モジュールを再度取得します
var deps = module.dependency
if (deps.length) {
// ループ依存関係チェック スタックを通じて、循環依存関係があるかどうかを確認します (これは依存関係モジュール チェックの最初の層で、メインとの循環依存関係です) module)
if (isOverlap(deps,circularCheckStack)) {
return true
}
// 上記の状況が存在しない場合は、依存モジュールの依存モジュールをさらにチェックして、循環依存関係チェック スタック内の URI のモジュールに対して循環依存関係があります
// この場合、それは再帰的で循環的です。依存関係チェック スタックはチェーンのようなものです。現在のモジュールはメイン モジュール、つまりメイン モジュールをチェックします。メインモジュールのモジュール...最上位のメインモジュールまで依存関係があるかどうかを判断します
for (var i = 0; i if (isCircularWaiting(cachedModules[ deps[i]])) {
return true
}
}
}
// そうでない場合 循環依存関係がある場合は、プッシュされたモジュール URI をポップアウトします
circularCheckStack.pop()
return false
}
// 循環警告ログを出力します
function printCircularLog (stack, type) {
util.log( '循環依存関係が見つかりました:', stack.join(' --> '), type)
}
//2 つの配列が重複しているかどうかを判断します
関数の値 isOverlap(arrA, arrB) {
var arrC = arrA.concat(arrB)
return arrC.length > util.unique(arrC).length
}
/ / 必要なモジュールがあるかどうかを設定ファイルから読み取ります。事前にロードされる
// プリロードされたモジュールがある場合は、まずプリロードされたモジュールを空に設定し (次回再度ロードする必要がないようにするため)、次の場合はプリロードされたモジュールをロードしてコールバックを実行します。そうでない場合は、順番に実行します。
function preload(callback) {
var preloadMods = config.preload.slice()
config.preload = []
preloadMods.length(preloadMods, callback ?) ) : callback()
}
// パブリック API
// 外部に公開される API
// ----------
// グローバル モジュールはページ モジュールと見なすことができます。ページ内の js および css ファイルはそれを通じてロードされます。
// モジュールの初期状態は COMPILED であり、uri はページの uri です。 🎜>var globalModule = new Module(util .pageUri, STATUS.COMPILED)
// ページ js、css ファイル ローダー
seajs.use = function(ids, callback) {
// ロード他のすべてのモジュールの前にモジュールをプリロードします。
// モジュールをプリロードします
preload(function() {
globalModule._use(ids, callback)
})
// Chain
return seajs
}
// 一般ユーザーの場合
// 一般ユーザーの場合
seajs.define = Module._define
seajs.cache = Module.キャッシュ
seajs.find = Module._find
seajs.modify = Module._modify
// プラグイン開発者向け
// 開発者向け
seajs.pluginSDK = {
モジュール: モジュール 、
util: util、
config: config
}
})(seajs, seajs._util, seajs._config)