组织项目
GWT 项目可以采用多种方式进行组织。但是,鼓励使用特定的约定,以便于识别哪些代码旨在运行在客户端浏览器、服务器还是两者。
本节介绍使用 GWT 组织项目的原理以及推荐的约定。
HTML 主页面
GWT 模块存储在 Web 服务器上,是一组 JavaScript 文件和相关文件。为了运行模块,它必须从某个网页加载。任何 HTML 页面都可以通过 SCRIPT
标签包含 GWT 应用程序。从 GWT 应用程序的角度来看,此 HTML 页面称为主机页面。从头开始用 GWT 编写的应用程序的典型 HTML 主页面可能根本不包含任何可见的 HTML 主体内容。以下示例显示了如何嵌入一个将使用整个浏览器窗口的 GWT 应用程序。
<html>
<head>
<!-- Properties can be specified to influence deferred binding -->
<meta name='gwt:property' content='locale=en_UK'>
<!-- Stylesheets are optional, but useful -->
<link rel="stylesheet" href="Calendar.css">
<!-- Titles are optional, but useful -->
<title>Calendar App</title>
</head>
<body>
<!-- This script tag is what actually loads the GWT module. The -->
<!-- 'nocache.js' file (also called a "selection script") is -->
<!-- produced by the GWT compiler in the module output directory -->
<!-- or generated automatically in development mode. -->
<script language="javascript" src="calendar/calendar.nocache.js"></script>
<!-- Include a history iframe to enable full GWT history support -->
<!-- (the id must be exactly as shown) -->
<iframe src="javascript:''" id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
</body>
</html>
请注意,页面的主体只包含一个 SCRIPT
标签和一个 IFRAME
标签。GWT 应用程序将填充所有可视内容。
但是,GWT 的设计使得只需进行少量更改,就可以轻松地将 GWT 功能添加到现有的 Web 应用程序中。可以允许 GWT 模块选择性地将小部件插入 HTML 页面中的特定位置。为了实现这一点,在 HTML 标签中使用 id
属性指定一个唯一的标识符,您的 GWT 代码将使用该标识符将小部件附加到该 HTML 元素。例如
<body>
<!-- ... other sample HTML omitted -->
<table align=center>
<tr>
<td id="slot1"></td>
<td id="slot2"></td>
</tr>
</table>
</body>
请注意,td
标签包含与其关联的 id
属性。此属性可以通过 DOM
类访问。您可以使用 RootPanel.get()
方法轻松地附加小部件。例如
final Button button = new Button("Click me");
final Label label = new Label();
...
RootPanel.get("slot1").add(button);
RootPanel.get("slot2").add(label);
通过这种方式,GWT 功能可以作为现有页面的一个部分添加,并且可以通过纯 HTML 更改应用程序布局。I18N
示例大量使用了此技术。
主机 HTML 页面不必是静态内容。它也可以由 servlet 或 JSP 页面生成。
标准目录和包布局
GWT 项目叠加在 Java 包上,以便可以从类路径和 模块定义 推断出大多数配置。
指南
如果您没有使用命令行工具生成项目文件和目录,请在组织代码和创建 Java 包时牢记以下指南。
- 在主项目目录下创建以下目录
-
- src 文件夹 - 包含生产 Java 源代码
- war 文件夹 - 您的 Web 应用程序;包含静态资源以及编译后的输出
- test 文件夹 - (可选)JUnit 测试代码将放在这里
- 在 src 包中,创建一个项目根包,并在其中创建一个客户端包。
- 如果您有服务器端代码,还需要创建一个服务器包,以区分客户端代码(将转换为 JavaScript)和服务器端代码(不会转换为 JavaScript)。
- 在项目根包中,放置一个或多个模块定义。
- 在 war 目录中,放置任何静态资源(例如主机页面、样式表或图像)。
- 在客户端包和服务器包中,您可以根据需要自由地组织代码到任何子包中。
示例:GWT 标准包布局
例如,“DynaTable” 示例的所有文件都组织在名为“DynaTable” 的主项目目录中。
- Java 源文件位于以下目录中:
DynaTable/src/com/google/gwt/sample/dynatable
- 模块定义在以下 XML 文件中:
DynaTable/src/com/google/gwt/sample/dynatable/DynaTable.gwt.xml
- 项目根包为:
com.google.gwt.sample.dynatable
- 逻辑模块名称为:
com.google.gwt.sample.dynatable.DynaTable
src 目录
src 目录包含应用程序的 Java 源文件、模块定义和外部资源文件。
包 | 文件 | 用途 |
---|---|---|
com.google.gwt.sample.dynatable |
项目根包包含模块 XML 文件。 | |
com.google.gwt.sample.dynatable |
DynaTable.gwt.xml |
您的应用程序模块。继承 com.google.gwt.user.User 并添加一个入口点类 com.google.gwt.sample.dynatable.client.DynaTable 。 |
com.google.gwt.sample.dynatable.public |
由 GWT 代码以编程方式加载的静态资源。public 目录中的文件将复制到与 GWT 编译器输出相同的目录中。 | |
com.google.gwt.sample.dynatable.public |
logo.gif |
应用程序代码可用的图像文件。您可以使用以下 URL 以编程方式加载此文件:GWT.getModuleBaseForStaticFiles() + "logo.gif" 。 |
com.google.gwt.sample.dynatable.client |
客户端源文件和子包。 | |
com.google.gwt.sample.dynatable.client |
DynaTable.java |
入口点类的客户端 Java 源代码。 |
com.google.gwt.sample.dynatable.client |
SchoolCalendarService.java |
RPC 服务接口。 |
com.google.gwt.sample.dynatable.server |
服务器端代码和子包。 | |
com.google.gwt.sample.dynatable.server |
SchoolCalendarServiceImpl.java |
实现服务逻辑的服务器端 Java 源代码。 |
war 目录
war 目录是 Web 应用程序的部署镜像。它采用标准的扩展 war 格式,被各种 Java Web 服务器(包括 Tomcat、Jetty 和其他 J2EE servlet 容器)识别。它包含各种资源
- 您提供的静态内容,例如主机 HTML 页面
- GWT 编译后的输出
- 服务器端代码的 Java 类文件和 jar 文件
- 配置 Web 应用程序和任何 servlet 的 web.xml 文件
war 格式的详细描述超出了本文档的范围,但以下列出了您需要了解的基本部分
目录 | 文件 | 用途 |
---|---|---|
DynaTable/war/ |
DynaTable.html |
加载 DynaTable 应用程序的主机 HTML 页面。 |
DynaTable/war/ |
DynaTable.css |
为 DynaTable 应用程序设置样式的静态样式表。 |
DynaTable/war/dynatable/ |
GWT 编译器写入输出和公共路径上的文件所在的 DynaTable 模块目录。注意:默认情况下,此目录将是完整的限定模块名称 com.google.gwt.sample.dynatable.DynaTable 。但是,在我们的 GWT 模块 XML 文件中,我们使用了 rename-to="dynatable" 属性将其缩短为一个简洁的名称。 |
|
DynaTable/war/dynatable/ |
dynatable.nocache.js |
DynaTable 的“选择脚本”。这是必须从主机 HTML 加载的脚本,以将 GWT 模块加载到页面中。 |
DynaTable/war/WEB-INF |
所有非公共资源都位于此处,有关更多详细信息,请参阅 servlet 规范。 | |
DynaTable/war/WEB-INF |
web.xml |
配置 Web 应用程序和任何 servlet。 |
DynaTable/war/WEB-INF/classes |
Java 编译后的类文件位于此处,用于实现服务器端功能。如果您使用的是 IDE,请将输出目录设置为该文件夹。 | |
DynaTable/war/WEB-INF/lib |
服务器代码需要的任何库依赖项都放在此处。 | |
DynaTable/war/WEB-INF/lib |
gwt-servlet.jar |
如果您有任何使用 GWT RPC 的 servlet,则需要在此处放置 gwt-servlet.jar 的副本。 |
test 目录
test 目录包含任何 JUnit 测试的源文件。
包 | 文件 | 用途 |
---|---|---|
com.google.gwt.sample.dynatable.client |
客户端测试文件和子包。 | |
com.google.gwt.sample.dynatable.client |
DynaTableTest.java |
入口点类的测试用例。 |
com.google.gwt.sample.dynatable.server |
服务器端测试文件和子包。 | |
com.google.gwt.sample.dynatable.server |
SchoolCalendarServiceImplTest.java |
服务器端类的测试用例。 |
模块:配置单元
GWT 配置的各个单元称为模块。模块将 GWT 项目所需的所有配置设置捆绑在一起
- 继承的模块
- 一个入口应用程序类名;这些是可选的,但 HTML 中引用的任何模块都必须指定至少一个入口点类。
- 源路径条目
- 公共路径条目
- 延迟绑定规则,包括属性提供者和类生成器
模块在 XML 中定义 并放置到您的 项目包层次结构 中。模块可以出现在类路径中的任何包中,但强烈建议它们出现在 标准项目布局 的根包中。
入口点类
模块入口点是任何可分配给 EntryPoint 的类,并且可以在没有参数的情况下构造。加载模块时,将实例化每个入口点类,并调用其 EntryPoint.onModuleLoad() 方法。
源路径
模块可以指定哪些子包包含可翻译的源代码,从而导致命名的包及其子包被添加到源路径中。只有在源路径上找到的文件才适合翻译成 JavaScript,这使得在同一个类路径中混合 客户端 和 服务器端 代码成为可能,而不会发生冲突。当模块继承其他模块时,它们的源路径会合并,以便每个模块都可以访问它所需的可翻译源代码。
默认源路径是在存储 模块 XML 文件 的位置下方的client 子包。
公共路径
模块可以指定哪些子包是公共的,从而导致命名的包及其子包被添加到公共路径中。公共路径是项目中存储 GWT 模块引用的静态资源(如 CSS 或图像)的位置。将应用程序编译成 JavaScript 时,将在模块的输出目录中复制在公共路径上找到的所有文件。在客户端代码中引用公共资源时(例如,设置 Image
小部件的 URL),您应该像这样构造 URL:GWT.getModuleBaseForStaticFiles() + "resourceName.png"
。在从 模块 XML 文件 中引用公共资源时,只需使用公共文件夹内的相对路径,模块的基本 URL 将自动附加。当模块继承其他模块时,它们的公共路径会合并,以便每个模块都可以访问它期望的静态资源。
默认公共路径是在存储 模块 XML 文件 的位置下方的public 子目录。
定义模块:模块 XML 文件的格式
模块在扩展名为.gwt.xml 的 XML 文件中定义。模块 XML 文件应位于项目的根包中。
如果您使用的是 标准项目结构,您的模块 XML 可以像以下示例一样简单
<module rename-to="dynatable">
<inherits name="com.google.gwt.user.User" />
<entry-point class="com.google.gwt.sample.dynatable.client.DynaTable" />
</module>
加载模块
模块 XML 文件位于 Java 类路径上。模块始终通过其逻辑名称来引用。模块的逻辑名称形式为pkg1.pkg2.ModuleName(尽管可能存在任意数量的包)。逻辑名称既不包括实际的文件系统路径,也不包括文件扩展名。
例如,如果模块 XML 文件的名称为…
~/src/com/example/cal/Calendar.gwt.xml
…那么模块的逻辑名称为
com.example.cal.Calendar
重命名模块
<module>
元素支持一个可选的属性 rename-to
,它会导致编译器表现得好像模块具有与长而完全限定的名称不同的名称。重命名模块有两个主要用例
- 拥有一个不反映实际包结构的较短的模块名称,这是最典型和推荐的用例
- 创建“工作模块”以通过限制排列的数量来加快开发时间
com.foo.WorkingModule.gwt.xml
:
<module rename-to="com.foo.MyModule">
<inherits name="com.foo.MyModule" />
<set-property name="user.agent" value="ie6" />
<set-property name="locale" value="default" />
</module>
当编译 WorkingModule.gwt.xml
时,编译器将仅使用默认语言环境生成 ie6
变体;这将加快开发编译的速度。WorkingModule.gwt.xml
的输出将是 MyModule.gwt.xml
的直接替换,因为编译器将使用备用名称生成输出。(当然,如果 com.foo.MyModule
本身被重命名,您只需复制其 rename-to
属性。)
将代码分成多个模块
创建第二个模块并不一定意味着该模块必须定义一个入口点。通常,当您想要打包一个要在其他 GWT 项目中重复使用的 GWT 代码库时,就会创建一个新模块。一个例子是 Google API 库 for GWT (GALGWT),特别是 Gears for GWT API 绑定。如果您下载该库并查看 gwt-google-apis/com/google/gwt/gears
,您会发现模块的 Gears.gwt.xml
文件,该文件没有定义入口点。但是,任何想要使用 Gears for GWT 的 GWT 项目都必须继承 Gears.gwt.xml 模块。例如,名为“Foo”的模块可能想要使用 GALGWT,因此在 Foo.gwt.xml
中,需要一个 <inherits>
条目
<module>
...
<inherits name='com.google.gwt.gears.Gears' />
在 HTML 主页中加载多个模块
如果您的应用程序中有多个 GWT 模块,有两种方法可以加载它们。
- 分别编译每个模块,并在您的 HTML 主页 中使用单独的
<script>
标签包含每个模块。 - 创建一个包含您要包含的所有模块的顶级模块 XML 定义。编译顶级模块以创建一组 JavaScript 输出。
第一种方法似乎最容易,也是最明显的。但是,第二种方法将带来更好的最终用户性能。加载多个模块的问题在于,每个模块都必须由最终用户的浏览器单独下载。此外,每个模块都将包含 GWT 库代码的冗余副本,并且可能在事件处理期间相互冲突。强烈建议使用第二种方法。
控制编译器输出
GWT 编译器将编译和打包其输出的行为与 Linker 子系统分开。它负责 JavaScript 代码的最终打包,并为任何特定部署场景提供可插入的引导机制。
有关更多信息,请参阅 链接器
使用另一个包实现覆盖一个包实现
<super-source>
标签指示编译器“重新定位”源路径。这对于您想要在 GWT 项目中重复使用现有 Java API,但原始源代码不可用或不可翻译的情况很有用。这样做的一个常见原因是模拟 GWT 未实现的 JRE 部分。
例如,假设您想实现 JRE 在 java.util
下提供的 UUID 类。假设您的项目的模块文件是 com/example/myproject/MyProject.gwt.xml
。将 UUID 类的源代码放置到 com/example/myproject/jre/java/util/UUID.java
中。然后在 MyProject.gwt.xml
中添加一行
<super-source path="jre" />
这告诉编译器将 com/example/myproject/jre/
的所有子文件夹添加到 源路径 中,但要剥离直到并包括 jre
的路径前缀。因此,com/google/myproject/gwt/jre/java/util/UUID.java
将对编译器可见为 java/util/UUID.java
,这正是预期的结果。
GWT 项目在内部使用此技术,用于随 GWT 提供的 JRE 模拟类。以这种方式覆盖 JRE 类的一个特定警告是它们永远不会在开发模式下实际使用。在开发模式下,本机 JRE 类始终优先于从源代码编译的类。
<super-source>
元素支持 基于模式的过滤,以允许对在 GWT 编译期间哪些资源被复制到输出目录进行细粒度控制。
XML 元素参考
本节记录了模块 XML 文件中最常用的元素。
<inherits name="_logical-module-name_" />
: 继承来自指定模块的所有设置,就好像继承的模块的 XML 内容被逐字复制一样。可以以这种方式继承任意数量的模块。另请参阅 关于决定继承哪些模块的建议。<entry-point class="_classname_" />
: 指定一个 入口点 类。可以添加任意数量的入口点类,包括来自继承模块的类。入口点都被编译成一个代码库。它们按它们在模块文件中出现的顺序依次调用。因此,当您的第一个入口点的onModuleLoad()
完成时,下一个入口点将立即被调用。<source path="_path_" />
:<source>
标签的每次出现都通过将模块 XML 所在的包与指定的子包路径组合,将一个包添加到 源路径 中。出现在此子包或其任何子包中的任何 Java 源文件都被认为是可翻译的。<source>
元素支持 基于模式的过滤,以允许对在 GWT 编译期间哪些资源被复制到输出目录进行细粒度控制。
如果在模块 XML 文件中没有定义
<source>
元素,则client 子包将隐式添加到源路径中,就好像在 XML 中找到了<source path="client" />
一样。此默认设置有助于使标准项目布局的模块 XML 保持简洁。
<public path="_path_" />
:<public>
标签的每次出现都通过将模块 XML 所在的包与指定的路径组合来将一个包添加到 公共路径 中,以标识公共路径条目的根目录。出现在此包或其任何子包中的任何文件都将被视为可公开访问的资源。<public>
元素支持 基于模式的过滤,以允许对在 GWT 编译期间哪些资源被复制到输出目录进行细粒度控制。
如果在模块 XML 文件中没有定义
<public>
元素,则public 子包将隐式添加到公共路径中,就好像在 XML 中找到了<public path="public">
一样。此默认设置有助于使标准项目布局的模块 XML 保持简洁。
<servlet path="_url-path_" class="_classname_" />
: 对于 RPC,此元素加载在指定 URL 路径上安装的 servlet 类。URL 路径应为绝对路径,并具有目录的形式(例如,/calendar
)。然后,您的客户端代码通过使用 @RemoteServiceRelativePath 属性注释服务接口来指定此 URL 映射。可以以这种方式加载任意数量的 servlet,包括来自继承模块的 servlet。
<servlet>
元素仅适用于 GWT 的嵌入式服务器服务器端调试功能。注意:从 GWT 1.6 开始,此标签不再在开发模式下加载 servlet,而是必须在 war 目录中配置一个
WEB-INF/web.xml
来加载所需的任何 servlet。
<script src="_js-url_" />
: 自动注入位于 src 指定位置的外部 JavaScript 文件。有关详细信息,请参阅 自动资源包含。如果指定的 URL 不是绝对的,则资源将从模块的基 URL 加载(换句话说,它很可能是一个公共资源)。<stylesheet src="_css-url_" />
: 自动注入位于 src 指定位置的外部 CSS 文件。有关详细信息,请参阅 自动资源包含。如果指定的 URL 不是绝对的,则资源将从模块的基 URL 加载(换句话说,它很可能是一个公共资源)。<extend-property name="_client-property-name_" values="_comma-separated-values_" />
: 扩展现有客户端属性的值集。可以通过这种方式添加任意数量的值,并且客户端属性值会通过继承的模块累积。您可能只会发现这对 在国际化中指定语言环境 有用。
延迟绑定元素
以下元素用于定义 延迟绑定 规则。延迟绑定在用户项目中并不常用。
<replace-with class="_replacement_class_name_">
: 使用替换进行延迟绑定的指令。<generate-with class="_generator_class_name_">
: 使用 Generator 进行延迟绑定的指令。<define-property name="_property_name_" values="_property_values_">
: 定义属性和允许的值(以逗号分隔的标识符)。此元素通常用于生成一个值,该值将由使用<when...>
元素的规则进行评估。<set-property name="_property_name_" value="_property_value_">
: 设置先前定义属性的值(请参见上面的<define property>
)。此元素通常用于生成一个值,该值将由使用<when...>
元素的规则进行评估。请注意,对同一值的set-property
和property-provider
将相互覆盖。最后遇到的定义是使用的定义。<property-provider name="_property_name_">
: 定义一个 JavaScript 片段,该片段将在运行时返回命名属性的值。此元素通常用于生成一个值,该值将在<when...>
元素中进行评估。要查看<property-provider>
定义在实际中的示例,请参阅 GWT 源代码中的I18N.gwt.xml
和UserAgent.gwt.xml
文件。请注意,对同一值的set-property
和property-provider
将相互覆盖。最后遇到的定义是使用的定义。
定义条件
<replace-with-class>
和 <generate-with-class>
元素可以接受一个 <when...>
子元素,该子元素定义何时应使用此规则,就像 SQL 查询的 WHERE
谓词一样。三种不同的谓词类型是
<when-property-is name="_property_name_" value="_value_" />
: 当命名属性具有给定值时为真的延迟绑定谓词。<when-type-assignable class="_class_name_" />
: 对类型系统中可分配给指定类型的类型为真的延迟绑定谓词。<when-type-is class="_class_name_" />
: 对类型系统中恰好一个类型为真的延迟绑定谓词。
可以将多个不同的谓词组合成一个表达式。使用以下嵌套元素的开始/结束标签将您的 <when...>
元素包围起来
<all>
when_expressions</all>
: 对所有子条件进行 AND 操作的谓词。<any>
when_expressions</any>
: 对所有子条件进行 OR 操作的谓词。<none>
when_expressions</none>
: 对所有子条件进行 NAND 操作的谓词。
延迟绑定示例
例如,使用延迟绑定规则的模块 XML 文件,以下是从 GWT 源代码中的 Focus.gwt.xml 文件中提取的模块 XML 文件
<module>
<inherits name="com.google.gwt.core.Core" />
<inherits name="com.google.gwt.user.UserAgent" />
<!-- old Mozilla, and Opera need a different implementation -->
<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplOld">
<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl" />
<any>
<when-property-is name="user.agent" value="gecko" />
<when-property-is name="user.agent" value="opera" />
</any>
</replace-with>
<!-- Safari needs a different hidden input -->
<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplSafari">
<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl" />
<when-property-is name="user.agent" value="safari" />
</replace-with>
<!-- IE's implementation traps exceptions on invalid setFocus() -->
<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplIE6">
<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl" />
<any>
<when-property-is name="user.agent" value="ie6" />
</any>
</replace-with>
</module>
我如何知道需要继承哪些 GWT 模块?
GWT 库被组织成模块。标准模块包含旨在独立于彼此工作的大块功能。通过仅选择项目所需的模块(例如 JSON 模块而不是 XML 模块),可以最大限度地降低复杂性并减少编译时间。
通常,您至少要继承 User 模块。User 模块包含所有核心 GWT 功能,包括 EntryPoint 类。User 模块还包含可重用的 UI 组件(小部件和面板)以及对 History 功能、国际化、DOM 编程等的支持。
标准模块 GWT 1.5
模块 | 逻辑名称 | 模块定义 | 内容 |
---|---|---|---|
User | com.google.gwt.user.User | User.gwt.xml | 核心 GWT 功能 |
HTTP | com.google.gwt.http.HTTP | HTTP.gwt.xml | 低级 HTTP 通信库 |
JSON | com.google.gwt.json.JSON | JSON.gwt.xml | JSON 创建和解析 |
JUnit | com.google.gwt.junit.JUnit | JUnit.gwt.xml | JUnit 测试框架集成 |
XML | com.google.gwt.xml.XML | XML.gwt.xml | XML 文档创建和解析 |
GWT 1.5 还提供了一些 主题 模块,其中包含小部件和面板的默认样式。您可以在项目的模块 XML 文件中指定一个主题作为样式化应用程序的起点,但您不必使用任何主题。
主题
模块 | 逻辑名称 | 模块定义 | 内容 |
---|---|---|---|
Chrome | com.google.gwt.user.theme.chrome.Chrome | Chrome.gwt.xml | Chrome 主题的样式表和图像。 |
Dark | com.google.gwt.user.theme.dark.Dark | Dark.gwt.xml | Dark 主题的样式表和图像。 |
Standard | com.google.gwt.user.theme.standard.Standard | Standard.gwt.xml | Standard 主题的样式表和图像。 |
如何操作
要继承一个模块,请编辑项目的模块 XML 文件并在 <inherits>
标签中指定要继承的模块的逻辑名称。
<inherits name="com.google.gwt.junit.JUnit"/>
注意: 模块始终以其逻辑名称引用。模块的逻辑名称形式为 pkg1.pkg2.ModuleName(尽管可能存在任意数量的包)。逻辑名称既不包含实际的文件系统路径,也不包含文件扩展名。
自动资源包含
模块可以包含对外部 JavaScript 和 CSS 文件的引用,导致在模块本身加载时自动加载这些文件。如果您的模块旨在用作可重用组件,这将非常方便,因为您的模块将不必依赖于 HTML 宿主页面来指定外部 JavaScript 文件或样式表。
包含外部 JavaScript
脚本包含是自动将外部 JavaScript 文件与您的模块关联的一种便捷方式。使用以下语法会导致外部 JavaScript 文件在调用模块入口点之前加载到 宿主页面 中。
<script src="_js-url_"/>
脚本被加载到 宿主页面 的命名空间中,就像您使用 HTML <script>
元素显式地包含它一样。在调用您的 onModuleLoad() 之前,将加载脚本。
1.4 之前的 GWT 版本需要一个脚本就绪函数来确定何时加载了包含的脚本。这不再需要;所有包含的脚本将在您的应用程序启动时加载,按照它们声明的顺序加载。
包含外部样式表
样式表包含是自动将外部 CSS 文件与您的模块关联的一种便捷方式。使用以下语法会导致 CSS 文件自动附加到 宿主页面。
<stylesheet src="_css-url_"/>
您可以通过这种方式添加任意数量的样式表,并且包含到页面的顺序反映了元素在您的模块 XML 中出现的顺序。
相对 URL 与绝对 URL
如果在 src
属性中指定了绝对 URL,则该 URL 将按字面意思使用。但是,如果使用了非绝对 URL(例如,“foo.css”),则模块的基 URL 将被附加到资源名称之前。这与在客户端代码中使用 GWT.getModuleBaseForStaticFiles() + "foo.css"
构造绝对 URL 相同。当目标资源来自模块的公共路径时,这很有用。
包含和模块继承
模块继承使资源包含特别方便。如果您希望创建一个依赖于特定样式表或 JavaScript 文件的可重用库,您可以确保库的客户端通过继承您的模块自动拥有他们需要的一切。
另请参阅
过滤公共和源包
模块 XML 格式 的 <public>
、<source>
和 <super-source>
元素支持某些属性和嵌套元素,以允许在 公共路径 中进行基于模式的包含和排除。这些元素遵循与 Ant 的 FileSet
元素相同的规则。请参阅 文档 以了解 FileSet
的一般概述。这些元素不支持完整的 FileSet
语义。当前仅支持以下属性和嵌套元素
includes
属性excludes
属性defaultexcludes
属性casesensitive
属性- 嵌套的
include
标签 - 嵌套的
exclude
标签
不支持其他属性和嵌套元素。
重要
defaultexcludes
的默认值为 true
。默认情况下,此处 列出的模式将被排除。
引导顺序
考虑以下加载 GWT 模块的 HTML 页面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body onload='alert("w00t!")'>
<img src='bigImageZero.jpg'></img>
<script source='externalScriptZero.js'></script>
<img src='bigImageOne.jpg'></img>
<img src='reallyBigImageTwo.jpg'></img>
<script src='myApp/myApp.nocache.js'></script>
<script src='externalScriptOne.js'></script>
</body>
</html>
以下原则需要理解此页面中将发生的运行操作顺序
<script>
标签始终阻塞页面的评估,直到脚本被获取并评估。<img>
标签**不会**阻塞页面评估。- 大多数浏览器将允许最多两个同时连接来获取资源。
body.onload()
事件仅在**所有**外部资源(包括图像和框架)被获取后才会触发。- GWT 选择脚本(即
myApp/myApp.nocache.js
)将像普通脚本标签一样被获取并评估,但编译后的脚本将**异步**获取。 - GWT 选择脚本开始运行后,其
onModuleLoad()
可以在外层文档解析后随时被调用。
将这些原则应用于上面的示例,我们得到以下顺序(请求完成的顺序可能会有很大差异,这只是其中一种可能发生的顺序,作为一个示例)
- 获取 HTML 文档并开始解析。
- 开始获取
bigImageZero.jpg
。 - 开始获取
externalScriptZero.js
。 bigImageZero.jpg
完成(假设)。在externalScriptZero.js
完成获取和评估之前,解析将被阻塞。externalScriptZero.js
完成。- 同时开始获取
bigImageOne.jpg
和reallyBigImageTwo.jpg
。 bigImageOne.jpg
完成(再次假设)。myApp/myApp.nocache.js
开始获取和评估。myApp/myApp.nocache.js
完成,编译后的脚本(<hashname>.cache.js
)开始在隐藏的IFRAME
中获取(这是非阻塞的)。<hashname>.cache.js
完成。onModuleLoad()
尚未被调用,因为我们还在等待externalScriptOne.js
完成,然后才能将文档视为“准备就绪”。externalScriptOne.js
完成。文档已准备就绪,因此 onModuleLoad() 被触发。reallyBigImageTwo.jpg
完成。body.onload()
触发,在这种情况下,将显示一个 alert() 框。
这有点复杂,但重点是展示各种资源何时被获取,以及 onModuleLoad()
何时被调用。最重要的是要记住:
- 您希望将 GWT 选择脚本尽可能早地放在 body 中,以便它在其他脚本之前开始获取编译后的脚本(因为它不会阻塞任何其他脚本请求)。
- 如果您要获取外部图像和脚本,则需要仔细管理您的两个连接。
<img>
标签在onModuleLoad()
被调用时,并不保证已经加载完成。<script>
标签在onModuleLoad()
被调用时,保证已经加载完成。
关于多个 GWT 模块和入口点的说明
如果您在一个模块中有多个入口点(定义 onModuleLoad()
的接口),它们将在该模块(以及外部文档)就绪后按顺序调用。
如果您在同一页面中加载多个 GWT 模块,则每个模块的入口点将在该模块和外部文档都就绪后被调用。两个模块的入口点并不保证同时触发,也不保证按照它们在宿主页面中指定的 selection 脚本的顺序触发。