GWT生成javascript后,WWW目录中会得到很多HTML和JS代码。这些代码的作用是什么对于每一个初次使用GWT的人来说,都会有这样的困惑。下面我将对这些文件的大概作用进行以下简单的说明,并对其中的部分JS进行分析。
首先说一下会生成哪些文件。以下是文件的列表。这个列表只包括HTML,和JS文件。这些文件都是单个GWT module编译后产生的。本文使用的原代码来自GWT论坛。如果想得到完全相同的文件列表,读者可以自行下载。
下载地址是: http://www.mooreds.com/files/mtgcalc-v1.tar.gz
文件列表:
1、MortgageCalc.html
2、gwt.js
3、history.html
4、com.cohomefinder.gwt.mortgagecalculator.MortgageCalc.nocache.html
5、31B37A91E12073356EFED6A87DDD05BB.cache.html
6、992FF46E12A7705BC86ACDF2A5956B7C.cache.html
7、BC136B40572B863559384961791144CA.cache.html
8、77A54215924094616B0083B14A757399.cache.html
这些都是些什么文件呢?下文将逐一介绍。
这个文件不用多说了,就是要插入编译后JS的目标HTML。GWT的JS插入不是在编译时进行的,而是在运行时由GWT定义的JS进行运行时插入。所以查看host page的源文件时只能看到以下内容:
<
meta
name
='gwt:module'
content
='com.cohomefinder.gwt.mortgagecalculator.MortgageCalc'
>
<
script
language
="javascript"
src
="gwt.js"
></
script
>
其中动态加载代码就在gwt.js中,而meta数据提供了必要的信息,帮助动态加载文件选择正确的代码加载。
//
//
DynamicResources
//
function
DynamicResources()
{
this.pendingElemsBySrc_ = {};
this.pendingScriptElems_ = new Array();
}
DynamicResources.prototype
=
{}
;

//
The array is set up such that, pairwise, the entries are (src, readyFnStr).
//
Called once for each module that is attached to the host page.
//
It is theoretically possible that addScripts() could be called reentrantly
//
if the browser event loop is pumped during this function and an iframe loads;
//
we may want to enhance this method in the future to support that case.
DynamicResources.prototype.addScripts
=
function
(scriptArray, insertBeforeElem)
{
var wasEmpty = (this.pendingScriptElems_.length == 0);
var anyAdded = false;
for (var i = 0, n = scriptArray.length; i < n; i += 2) {
var src = scriptArray[i];
if (this.pendingElemsBySrc_[src]) {
// Don't load the same script twice.
continue;
}
// Set up the element but don't add it to the DOM until its turn.
anyAdded = true;
var e = document.createElement("script");
this.pendingElemsBySrc_[src] = e;
var readyFn;
eval("readyFn = " + scriptArray[i+1]);
e.__readyFn = readyFn;
e.type = "text/javascript";
e.src = src;
e.__insertBeforeElem = insertBeforeElem;
this.pendingScriptElems_ = this.pendingScriptElems_.concat(e);
}
if (wasEmpty && anyAdded) {
// Kickstart.
this.injectScript(this.pendingScriptElems_[0]);
}
}

DynamicResources.prototype.injectScript
=
function
(scriptElem)
{
var parentElem = scriptElem.__insertBeforeElem.parentNode;
parentElem.insertBefore(scriptElem, scriptElem.__insertBeforeElem);
}

DynamicResources.prototype.addStyles
=
function
(styleSrcArray, insertBeforeElem)
{
var parent = insertBeforeElem.parentNode;
for (var i = 0, n = styleSrcArray.length; i < n; ++i) {
var src = styleSrcArray[i];
if (this.pendingElemsBySrc_[src])
continue;
var e = document.createElement("link");
this.pendingElemsBySrc_[src] = e;
e.type = "text/css";
e.rel = "stylesheet";
e.href = src;
parent.insertBefore(e, insertBeforeElem);
}
}

DynamicResources.prototype.isReady
=
function
()
{
var elems = this.pendingScriptElems_;
if (elems.length > 0) {
var e = elems[0];
if (!e.__readyFn()) {
// The pending script isn't ready yet.
return false;
}
// The pending script has now finished loading. Enqueue the next, if any.
e.__readyFn = null;
elems.shift();
if (elems.length > 0) {
// There is another script.
this.injectScript(elems[0]);
return false;
}
}

// There are no more pending scripts.
return true;
}
这个类主要的功能是向host page中插入一些JS和CSS。
//
//
ModuleControlBlock
//
function
ModuleControlBlock(metaElem, rawName)
{
var parts = ["", rawName];
var i = rawName.lastIndexOf("=");
if (i != -1) {
parts[0] = rawName.substring(0, i) + '/';
parts[1] = rawName.substring(i+1);
}

this.metaElem_ = metaElem;
this.baseUrl_ = parts[0];
this.name_ = parts[1];
this.compilationLoaded_ = false;
this.frameWnd_ = null;
}
ModuleControlBlock.prototype
=
{}
;

/**
* Determines whether this module is fully loaded and ready to run.
*/
ModuleControlBlock.prototype.isReady
=
function
()
{
return this.compilationLoaded_;
}
;

/**
* Called when the compilation for this module is loaded.
*/
ModuleControlBlock.prototype.compilationLoaded
=
function
(frameWnd)
{
alert(frameWnd);
this.frameWnd_ = frameWnd;
this.compilationLoaded_ = true;
}

/**
* Gets the logical module name, not including a base url prefix if one was
* specified.
*/
ModuleControlBlock.prototype.getName
=
function
()
{
return this.name_;
}

/**
* Gets the base URL of the module, guaranteed to end with a slash.
*/
ModuleControlBlock.prototype.getBaseURL
=
function
()
{
return this.baseUrl_;
}

/**
* Gets the window of the module's frame.
*/
ModuleControlBlock.prototype.getModuleFrameWindow
=
function
()
{
return this.frameWnd_;
}

/**
* Injects a set of dynamic scripts.
* The array is set up such that, pairwise, the entries are (src, readyFnStr).
*/
ModuleControlBlock.prototype.addScripts
=
function
(scriptSrcArray)
{
alert(scriptSrcArray);
return ModuleControlBlocks.dynamicResources_.addScripts(scriptSrcArray, this.metaElem_);
}

/**
* Injects a set of dynamic styles.
*/
ModuleControlBlock.prototype.addStyles
=
function
(styleSrcArray)
{
return ModuleControlBlocks.dynamicResources_.addStyles(styleSrcArray, this.metaElem_);
}
一个
ModuleControlBlock的实例描述了一个gwt模块的信息。一个host page可以载入并运行多个gwt模块,
每个
gwt模块都会用一个
ModuleControlBlock来描述
。gwt模块都有一个名字来区分,模块的名字就是java源代码中的java类的fullly qualified class name(完整类名)。
//
//
ModuleControlBlocks
//
function
ModuleControlBlocks()
{
this.blocks_ = [];
}
ModuleControlBlocks.dynamicResources_
=
new
DynamicResources();
//
"static"
ModuleControlBlocks.prototype
=
{}
;

/**
* Adds a module control control block for the named module.
* @param metaElem the meta element that caused the module to be added
* @param name the name of the module being added, optionally preceded by
* an alternate base url of the form "_path_=_module_".
*/
ModuleControlBlocks.prototype.add
=
function
(metaElem, name)
{
var mcb = new ModuleControlBlock(metaElem, name);
this.blocks_ = this.blocks_.concat(mcb);
}
;

/**
* Determines whether all the modules are loaded and ready to run.
*/
ModuleControlBlocks.prototype.isReady
=
function
()
{
for (var i = 0, n = this.blocks_.length; i < n; ++i) {
var mcb = this.blocks_[i];
if (!mcb.isReady()) {
return false;
}
}
// Are there any pending dynamic resources (e.g. styles, scripts)?
if (!ModuleControlBlocks.dynamicResources_.isReady()) {
// No, we're still waiting on one or more dynamic resources.
return false;
}

return true;
}

/**
* Determines whether there are any module control blocks.
*/
ModuleControlBlocks.prototype.isEmpty
=
function
()
{
return this.blocks_.length == 0;
}

/**
* Gets the module control block at the specified index.
*/
ModuleControlBlocks.prototype.get
=
function
(index)
{
return this.blocks_[index];
}

/**
* Injects an iframe for each module.
*/
ModuleControlBlocks.prototype.injectFrames
=
function
()
{
for (var i = 0, n = this.blocks_.length; i < n; ++i) {
var mcb = this.blocks_[i];

// Insert an iframe for the module
var iframe = document.createElement("iframe");
var selectorUrl = mcb.getBaseURL() + mcb.getName() + ".nocache.html";
selectorUrl += "?" + (__gwt_isHosted() ? "h&" : "" ) + i;
var unique = new Date().getTime();
selectorUrl += "&" + unique;
iframe.style.border = '0px';
iframe.style.width = '0px';
iframe.style.height = '0px';
alert(selectorUrl);
// Fragile browser-specific ordering issues below
/*@cc_on
// prevent extra clicky noises on IE
iframe.src = selectorUrl;
@*/
if (document.body.firstChild) {
document.body.insertBefore(iframe, document.body.firstChild);
} else {
document.body.appendChild(iframe);
}
/*@cc_on
// prevent extra clicky noises on IE
return;
@*/

if (iframe.contentWindow) {
// Older Mozilla has a caching bug for the iframe and won't reload the nocache.
iframe.contentWindow.location.replace(selectorUrl);
} else {
// Older Safari doesn't have a contentWindow.
iframe.src = selectorUrl;
}
}
}

/**
* Runs the entry point for each module.
*/
ModuleControlBlocks.prototype.run
=
function
()
{
for (var i = 0, n = this.blocks_.length; i < n; ++i) {
var mcb = this.blocks_[i];
var name = mcb.getName();
var frameWnd = mcb.getModuleFrameWindow();
if (__gwt_isHosted()) {
if (!window.external.gwtOnLoad(frameWnd, name)) {
// Module failed to load.
if (__gwt_onLoadError) {
__gwt_onLoadError(name);
} else {
window.alert("Failed to load module '" + name +
"'. Please see the log in the development shell for details.");
}
}
} else {
// The compilation itself handles calling the error function.
frameWnd.gwtOnLoad(__gwt_onLoadError, name);
}
}
}
ModuleControlBlocks不是
ModuleControlBlock的一个集合,并且会负责将控制快所描述的模块载入并运行。每一个gwt模块都运行在单独的iframe中。
31B37A91E12073356EFED6A87DDD05BB.cache.html:
针对gecko1_8 或者 gecko浏览器的JS生成文件。
992FF46E12A7705BC86ACDF2A5956B7C.cache.html:
针对ie6 的JS生成文件。
BC136B40572B863559384961791144CA.cache.html:
针对 safari 的JS生成文件。
77A54215924094616B0083B14A757399.cache.html:
针对 opera 的JS生成文件。
首先说一下会生成哪些文件。以下是文件的列表。这个列表只包括HTML,和JS文件。这些文件都是单个GWT module编译后产生的。本文使用的原代码来自GWT论坛。如果想得到完全相同的文件列表,读者可以自行下载。
下载地址是: http://www.mooreds.com/files/mtgcalc-v1.tar.gz
文件列表:
1、MortgageCalc.html
2、gwt.js
3、history.html
4、com.cohomefinder.gwt.mortgagecalculator.MortgageCalc.nocache.html
5、31B37A91E12073356EFED6A87DDD05BB.cache.html
6、992FF46E12A7705BC86ACDF2A5956B7C.cache.html
7、BC136B40572B863559384961791144CA.cache.html
8、77A54215924094616B0083B14A757399.cache.html
这些都是些什么文件呢?下文将逐一介绍。
MortgageCalc.html
这个文件不用多说了,就是要插入编译后JS的目标HTML。GWT的JS插入不是在编译时进行的,而是在运行时由GWT定义的JS进行运行时插入。所以查看host page的源文件时只能看到以下内容:


gwt.js:
这就是上面所说提到的文件。里面的代码主要是用来动态加载编译后的JS代码的。这10页的JS代码中,一共定义了3个类:DynamicResources,ModuleControlBlock, ModuleControlBlock。





























































































































































模块在编译后,会生成一个与模块名字对应的,以nocache.html结尾的一个HTML文件。在本文中就是com.cohomefinder.gwt.mortgagecalculator.MortgageCalc.nocache.html。
载入gwt模块时只要在host page里申明一个meta标签就可以了。meta 的name属性是“gwt:module”,content属性是模块的完整名字。



























































































































history.html:
com.cohomefinder.gwt.mortgagecalculator.MortgageCalc.nocache.html:
这个文件是一个加载代理,它是与浏览器无关的代码。由于目前市场上浏览器的种类繁多。所以GWT在编译时会产生针对不同浏览器的不同版本的JS生成文件。而模块加载程序首先加载的就是这个不一览浏览器的代码文件,其中包含了的程序可以根据浏览器的种类,再由它来选择的和浏览器有关的那份代码。
31B37A91E12073356EFED6A87DDD05BB.cache.html:
针对gecko1_8 或者 gecko浏览器的JS生成文件。
992FF46E12A7705BC86ACDF2A5956B7C.cache.html:
针对ie6 的JS生成文件。
BC136B40572B863559384961791144CA.cache.html:
针对 safari 的JS生成文件。
77A54215924094616B0083B14A757399.cache.html:
针对 opera 的JS生成文件。