Ext.Loaderソース
ext/src/class/Loader.js
主な方法、require、以下の通りです.
passメソッドは、次のとおりです.
loadScriptFileメソッドは、次のとおりです.
injectScriptElementメソッド:
onFileLoadedメソッドは、次のとおりです. require('soims.view.user.UserGrid',function(userGrid){//ロードされ作成されたuserGridインスタンスを使用する});
2.if(obj.hasOwnProperty(per))とif(per in obj)の違いは何ですか?
3.pass(fn,[parm 1,parm 2])メソッドは、追加のパラメータを受け入れることができるfnを返します.詳細はソースコードを参照してください.
onFileLoadedはファイルのロードに成功したコールバック関数で、詳しくはソースコードを参照してください.
4.JSファイルのロード、詳細はソースコードを参照
5.htmlにタグを追加し、詳細はソースコードを参照
6.コールバック関数が実行されたかどうかを示す.なぜ表示する必要があるのか.コールバック関数は何度もトリガーされますか?
7.ここではコメント3のpassの戻り関数を呼び出します
passコードは次のとおりです.
パッケージを閉じることでクラス名とパス情報を保存し、コールバック関数が実行されると、この2つのパラメータをコールバック関数のパラメータに配置し、コールバック関数に使用します.
ここでのコールバック関数は、次のとおりです.
簡単に言えば require()メソッドでは、injectScriptElement()メソッドが必要であり、onFileLoadedをコールバック関数としてinjectScriptElement に渡すしかし、コールバック関数onFileLoadedでは、requireのローカル変数 が必要です.したがって、変数とコールバック関数を一時的に閉パケット関数passに格納し、コールバック関数が実行されると、閉パケット中の変数 にアクセスできる.
ここのpass()メソッドは、delegateに依頼するのではないでしょうか.
ファイルの非同期ロードの主なプロセスは、次のとおりです.
ブラウザ・ロード・ファイル->ロードが完了したら解析、実行ファイル->Ext.define()の実行->ブロッキング・プロセスの実行->Loader processorの実行->依存関係dependenciesの抽出->require()メソッドを呼び出して個々のロード・ファイルを削除->htmlにラベルを個々に追加->ブラウザ・ロード・ファイル
ファイルの読み込み、解析、実行が完了すると、コールバック関数onFileLoadedが実行されます.
主な方法、require、以下の通りです.
require: function(expressions, fn, scope, excludes) {
var excluded = {},
included = {},
excludedClassNames = [],
possibleClassNames = [],
classNames = [],
references = [],
callback,
syncModeEnabled,
filePath, expression, exclude, className,
possibleClassName, i, j, ln, subLn;
//
if (excludes) {
// Convert possible single string to an array.
excludes = (typeof excludes === 'string') ? [ excludes ] : excludes;
for (i = 0,ln = excludes.length; i < ln; i++) {
exclude = excludes[i];
if (typeof exclude == 'string' && exclude.length > 0) {
excludedClassNames = Manager.getNamesByExpression(exclude);
for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) {
excluded[excludedClassNames[j]] = true;
}
}
}
}
// Convert possible single string to an array.
expressions = (typeof expressions === 'string') ? [ expressions ] : (expressions ? expressions : []); // perfect
/**
* , , , 1
*/
if (fn) {
if (fn.length > 0) {
callback = function() {
var classes = [],
i, ln;
for (i = 0,ln = references.length; i < ln; i++) {
classes.push(Manager.get(references[i]));
}
return fn.apply(this, classes);
};
}
else {
callback = fn;
}
}
else {
callback = Ext.emptyFn;
}
scope = scope || Ext.global;
for (i = 0,ln = expressions.length; i < ln; i++) {
expression = expressions[i];
if (typeof expression == 'string' && expression.length > 0) {
possibleClassNames = Manager.getNamesByExpression(expression);
subLn = possibleClassNames.length;
for (j = 0; j < subLn; j++) {
possibleClassName = possibleClassNames[j];
if (excluded[possibleClassName] !== true) {
references.push(possibleClassName);
if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) {
included[possibleClassName] = true;
classNames.push(possibleClassName);
}
}
}
}
}
// If the dynamic dependency feature is not being used, throw an error
// if the dependencies are not defined
if (classNames.length > 0) {
if (!Loader.config.enabled) {
throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
"Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', '));
}
}
else {
callback.call(scope);
return Loader;
}
syncModeEnabled = Loader.syncModeEnabled;
//
if (!syncModeEnabled) {
queue.push({
requires: classNames.slice(), // this array will be modified as the queue is processed,
// so we need a copy of it
callback: callback,
scope: scope
});
}
ln = classNames.length;
for (i = 0; i < ln; i++) {
className = classNames[i];
filePath = Loader.getPath(className); // ,
// If we are synchronously loading a file that has already been asychronously loaded before
// we need to destroy the script tag and revert the count
// This file will then be forced loaded in synchronous
if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {
if (!isClassFileLoaded[className]) {
Loader.numPendingFiles--;
Loader.removeScriptElement(filePath);
delete isClassFileLoaded[className];
}
}
if (!isClassFileLoaded.hasOwnProperty(className)) { // 2
isClassFileLoaded[className] = false;
classNameToFilePathMap[className] = filePath;
Loader.numPendingFiles++;
Loader.loadScriptFile(
filePath,
pass(Loader.onFileLoaded, [className, filePath], Loader),// 3
pass(Loader.onFileLoadError, [className, filePath], Loader),
Loader,
syncModeEnabled
); // 4
}
}
if (syncModeEnabled) {
callback.call(scope);
if (ln === 1) {
return Manager.get(className);
}
}
return Loader;
}
passメソッドは、次のとおりです.
/**
* Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
* New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
* This is especially useful when creating callbacks.
*
* For example:
*
* var originalFunction = function(){
* alert(Ext.Array.from(arguments).join(' '));
* };
*
* var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
*
* callback(); // alerts 'Hello World'
* callback('by Me'); // alerts 'Hello World by Me'
*
* {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
*
* @param {Function} fn The original function
* @param {Array} args The arguments to pass to new callback
* @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
* @return {Function} The new callback function
*/
pass: function(fn, args, scope) {
if (!Ext.isArray(args)) {
if (Ext.isIterable(args)) {
args = Ext.Array.clone(args);
} else {
args = args !== undefined ? [args] : [];
}
}
return function() {
var fnArgs = [].concat(args);
fnArgs.push.apply(fnArgs, arguments);
return fn.apply(scope || this, fnArgs);
};
},
loadScriptFileメソッドは、次のとおりです.
loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
if (isFileLoaded[url]) {
return Loader;
}
var config = Loader.getConfig(),
noCacheUrl = url + (config.disableCaching ? ('?' + config.disableCachingParam + '=' + Ext.Date.now()) : ''), // ,
isCrossOriginRestricted = false,
xhr, status, onScriptError,
debugSourceURL = "";
scope = scope || Loader;
Loader.isLoading = true;
if (!synchronous) { //
onScriptError = function() {
onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
};
scriptElements[url] = Loader.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope); // 5
} else { //
if (typeof XMLHttpRequest != 'undefined') {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
try {
xhr.open('GET', noCacheUrl, false);
xhr.send(null);
} catch (e) {
isCrossOriginRestricted = true;
}
status = (xhr.status === 1223) ? 204 :
(xhr.status === 0 && ((self.location || {}).protocol == 'file:' || (self.location || {}).protocol == 'ionp:')) ? 200 : xhr.status;
isCrossOriginRestricted = isCrossOriginRestricted || (status === 0);
if (isCrossOriginRestricted
) {
onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
"being loaded from a different domain or from the local file system whereby cross origin " +
"requests are not allowed due to security reasons. Use asynchronous loading with " +
"Ext.require instead.", synchronous);
}
else if ((status >= 200 && status < 300) || (status === 304)
) {
// Debugger friendly, file names are still shown even though they're eval'ed code
// Breakpoints work on both Firebug and Chrome's Web Inspector
if (!Ext.isIE) {
debugSourceURL = "
//@ sourceURL=" + url;
}
Ext.globalEval(xhr.responseText + debugSourceURL);
onLoad.call(scope);
}
else {
onError.call(Loader, "Failed loading synchronously via XHR: '" + url + "'; please " +
"verify that the file exists. " +
"XHR status code: " + status, synchronous);
}
// Prevent potential IE memory leak
xhr = null;
}
}
injectScriptElementメソッド:
injectScriptElement: function(url, onLoad, onError, scope, charset) {
var script = document.createElement('script'),
dispatched = false,
config = Loader.config,
onLoadFn = function() {
if(!dispatched) { // 6
dispatched = true;
script.onload = script.onreadystatechange = script.onerror = null;
if (typeof config.scriptChainDelay == 'number') {
//free the stack (and defer the next script)
defer(onLoad, config.scriptChainDelay, scope);
} else {
onLoad.call(scope); // 7
}
Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
}
},
onErrorFn = function(arg) {
defer(onError, 1, scope); //free the stack
Loader.cleanupScriptElement(script, config.preserveScripts === false, config.garbageCollect);
};
script.type = 'text/javascript';
script.onerror = onErrorFn;
charset = charset || config.scriptCharset;
if (charset) {
script.charset = charset;
}
/*
* IE9 Standards mode (and others) SHOULD follow the load event only
* (Note: IE9 supports both onload AND readystatechange events)
*/
if ('addEventListener' in script ) {
script.onload = onLoadFn;
} else if ('readyState' in script) { // for <IE9 Compatability
script.onreadystatechange = function() {
if ( this.readyState == 'loaded' || this.readyState == 'complete' ) {
onLoadFn();
}
};
} else {
script.onload = onLoadFn;
}
script.src = url;
(Loader.documentHead || document.getElementsByTagName('head')[0]).appendChild(script);
return script;
}
onFileLoadedメソッドは、次のとおりです.
onFileLoaded: function(className, filePath) {
var loaded = isClassFileLoaded[className];
Loader.numLoadedFiles++;
isClassFileLoaded[className] = true; //
isFileLoaded[filePath] = true; //
// In FF, when we sync load something that has had a script tag inserted, the load event may
// sometimes fire even if we clean it up and set it to null, so check if we're already loaded here.
if (!loaded) {
Loader.numPendingFiles--;
}
if (Loader.numPendingFiles === 0) {
Loader.refreshQueue();
}
//<debug>
if (!Loader.syncModeEnabled && Loader.numPendingFiles === 0 && Loader.isLoading && !Loader.hasFileLoadError) {
var missingClasses = [],
missingPaths = [],
requires,
i, ln, j, subLn;
for (i = 0,ln = queue.length; i < ln; i++) {
requires = queue[i].requires;
for (j = 0,subLn = requires.length; j < subLn; j++) {
if (isClassFileLoaded[requires[j]]) {
missingClasses.push(requires[j]);
}
}
}
if (missingClasses.length < 1) {
return;
}
missingClasses = Ext.Array.filter(Ext.Array.unique(missingClasses), function(item) {
return !requiresMap.hasOwnProperty(item);
}, Loader);
if (missingClasses.length < 1) {
return;
}
for (i = 0,ln = missingClasses.length; i < ln; i++) {
missingPaths.push(classNameToFilePathMap[missingClasses[i]]);
}
throw new Error("The following classes are not declared even if their files have been " +
"loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
"corresponding files for possible typos: '" + missingPaths.join("', '"));
}
//</debug>
}
2.if(obj.hasOwnProperty(per))とif(per in obj)の違いは何ですか?
3.pass(fn,[parm 1,parm 2])メソッドは、追加のパラメータを受け入れることができるfnを返します.詳細はソースコードを参照してください.
onFileLoadedはファイルのロードに成功したコールバック関数で、詳しくはソースコードを参照してください.
4.JSファイルのロード、詳細はソースコードを参照
5.htmlに
6.コールバック関数が実行されたかどうかを示す.なぜ表示する必要があるのか.コールバック関数は何度もトリガーされますか?
7.ここではコメント3のpassの戻り関数を呼び出します
passコードは次のとおりです.
pass: function(fn, args, scope) {
if (!Ext.isArray(args)) {
if (Ext.isIterable(args)) {
args = Ext.Array.clone(args);
} else {
args = args !== undefined ? [args] : [];
}
}
return function() {
var fnArgs = [].concat(args);
fnArgs.push.apply(fnArgs, arguments);
return fn.apply(scope || this, fnArgs);
};
}
パッケージを閉じることでクラス名とパス情報を保存し、コールバック関数が実行されると、この2つのパラメータをコールバック関数のパラメータに配置し、コールバック関数に使用します.
ここでのコールバック関数は、次のとおりです.
function() {
var fnArgs = [].concat(args);
fnArgs.push.apply(fnArgs, arguments);
return fn.apply(scope || this, fnArgs);
}
簡単に言えば
ここのpass()メソッドは、delegateに依頼するのではないでしょうか.
ファイルの非同期ロードの主なプロセスは、次のとおりです.
ブラウザ・ロード・ファイル->ロードが完了したら解析、実行ファイル->Ext.define()の実行->ブロッキング・プロセスの実行->Loader processorの実行->依存関係dependenciesの抽出->require()メソッドを呼び出して個々のロード・ファイルを削除->htmlに
ファイルの読み込み、解析、実行が完了すると、コールバック関数onFileLoadedが実行されます.