领先一步
VMware 提供培训和认证,助您加速进步。
了解更多Web 应用程序通常严重依赖我们称之为静态资源的资源,例如 Javascript、CSS 和图像文件。在 Grails 应用程序中,它们被放入项目的web-app目录中,然后从 HTML 中引用。例如,
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
将创建指向文件web-app/css/main.css的链接。所有这些都非常简单。您甚至可能认为当前的支持足以满足任何人的需求。您还想做什么?
这是一个很好的观点。答案取决于您的应用程序的复杂性,但让我们从上面的 CSS 链接示例开始。为什么我们必须键入<link rel="..." href=...>通过查看扩展名,我们知道该资源是一个 CSS 文件。我们也知道 CSS 文件应该使用上面的元素链接到 HTML 页面中。所以我们基本上是在为 Grails 本身应该能够处理的事情做很多重复的输入。
“好吧,”我听到你这样说,“但我们可以为 CSS 链接添加一个标签。”确实可以,Grails 2 将包含一个<g:external/>标签,它能智能地为资源选择合适的链接类型。但现在考虑生产环境:将 CSS 和 Javascript 文件捆绑在一起不是个好主意吗?压缩它们呢?(顺便说一句,Yahoo 提供了一个名为 ySlow 的工具,可以为你提供有关这类优化的提示)。那么我们如何实现这些优化呢?你可能会临时做,但这样做会限制你的选择,因为你无法定义资源之间的关系。我们真正想要的是一种在中心位置声明资源并指明哪些资源应该一起处理和捆绑的方法。
如果我还没有说服你存在更好的管理静态资源的方法,那么请考虑 Grails 插件。许多插件提供自己的静态资源。事实上,有些插件提供相同的资源。例如,旧版本的 YUI、Bubbling 和 Grails UI 插件都包含 YUI 库。我们真的需要多个完整的 Javascript 库副本吗?当然不需要。部分问题可以通过插件间的依赖来解决,但这本身效果不佳,因为插件无法声明其某些资源依赖于另一个插件中的特定资源。最终,开发人员需要确保所有适当的资源(包括传递依赖)都包含在每个页面中。这可能需要一些反复试验,以确保所有必需的资源都链接到页面中并且顺序正确。
幸运的是,我们可以做得更好。进入 Resources 插件。
只需安装这一个插件,你就可以开始在可重用模块中声明你的静态资源(Javascript、CSS 等)。然后你可以定义模块之间的依赖关系,这样 Grails 就确切地知道一个模块需要哪些资源,包括在传递依赖中指定的资源。额外的好处是,模块确保资源始终以正确的顺序在页面中声明,并且没有重复。通过这样做,模块大大减轻了静态资源管理的痛苦。
你可以在应用程序和插件中声明资源模块。有几种方法可以做到这一点,但最常见的方法是向项目添加一个或多个专用工件。对于应用程序来说,这可能是grails-app/conf/ApplicationResources.groovy。另一个例子是 YUI 插件有grails-app/conf/YuiPluginResources.groovy。这些文件的基本结构如下
modules = {
core {
resource url: 'js/core.js', disposition: 'head'
resource url: 'js/ui.js'
resource url: 'css/main.css'
resource url: 'css/branding.css'
resource url: 'css/print.css', attrs: [media: 'print']
}
utils {
dependsOn 'jquery'
resource url: 'js/utils.js'
}
forms {
dependsOn 'core', 'utils'
resource url: 'css/forms.css'
resource url: 'js/forms.js'
}
}
"core"、"utils" 和 "forms" 是我们应用程序模块的名称,你可能已经猜到,"forms" 模块依赖于 "core" 和 "utils"。你还可以看到 "utils" 模块依赖于 "jquery",这是一个由 jQuery 插件提供的模块。这种结构最好通过图表形式展现
如果我们深入研究上述模块定义,我们可以看到单个资源是使用“resource”加上 URL 在模块中声明的。此 URL 是资源相对于项目中的web-app目录的位置。如果需要,你还可以添加额外属性,以实现对资源的精细控制,特别是通过“disposition”设置。
有两种标准处置方式:“head”,表示资源位于 <head> 元素内;以及“defer”,通常表示位于 body 的末尾(尽管你可以控制确切的放置位置,稍后会看到)。默认情况下,CSS 文件具有“head”处置,而 Javascript 文件使用“defer”,但这些默认值可以按资源覆盖。
我们现在有了定义简单或复杂资源模块网络的基础,这些网络跨越应用程序和插件边界。从用户的角度来看,更好的是,插件负责确保其模块包含适当的资源和依赖项。
因此,Grails 现在可能已经知道你的应用程序及其插件提供了哪些静态资源,但它仍然不知道哪些页面需要哪些资源。为此,你必须对你的 GSP 进行一些修改。
如你所知,你以前必须在布局和视图中显式声明所有 CSS 和 Javascript 链接。那么使用 Resources 插件后,这会如何改变呢?你不再将链接放入单个资源中,而是声明你的页面需要哪些模块,这使得<head>块更加简洁。此外,你还可以指定资源链接在页面中的位置。这是一个使用 Resources 的非常简单的布局示例
<html>
<head>
...
<r:require modules="common, jquery"/>
<r:layoutResources/>
</head>
<body>
...
<r:layoutResources/>
</body>
</html>
如你所见,<r:require>标签告诉 Grails 要在页面中包含哪些静态资源,而<r:layoutResources>标签指定链接应该放在哪里。你应该有两个<r:layoutResources>标签:一个在<head>中,另一个在<body>中。任何处置方式为“head”的资源将放在第一个标签的位置,而处置方式为“defer”的资源将插入到第二个标签的位置。
就是这样!大部分工作都花在确保模块定义正确上。
到目前为止我所介绍的对于 CSS、Javascript 和 favicon 文件非常有效,因为 Resources 插件知道为它们生成什么链接以及将这些链接放在哪里。但是内联图像和脚本会发生什么呢?
Resources 插件将内联图像和脚本称为“临时资源”。这些通常不会在模块中声明,而是在页面中遇到时简单地进行处理。一个标准的内联图像链接看起来像
<img src="${resource(dir:'images',file:'grails_logo.png')}" ... />
那么我们如何让 Resources 插件知道图像文件呢?我们不需要!从 Grails 2.0 开始,<g:resource/>标签(上面用作方法)如果安装了插件,会自动向插件注册资源。这意味着映射器执行的所有魔法(稍后我会谈到)都将应用于给定资源。当然,<g:resource/>可以用于任何类型的资源,而不仅仅是图像。
内联脚本有点不同,因为它们不是外部文件的链接。但是它们仍然可以从 Resources 插件中受益。默认情况下,所有使用<g:javascript/>标签声明的内联脚本将像往常一样运行。但是,如果你将<g:javascript/>替换为<r:script/>标签,则内联脚本将被移动到第二个<r:layoutResources/>的位置(它们的默认处置方式是“defer”)。你还可以声明一个明确的处置方式,以便脚本进入<head>。最棒的是,内联脚本保留了它们在页面中声明的顺序。
还有一种临时资源需要提及:CSS。样式表是指定背景和其他类型图像的常见方式,但 CSS 文件不作为 GSP 文件处理。Resources 插件是否知道这些链接?幸运的是,是的。尽管 Resources 插件会修改它管理的所有资源的 URL 路径,但这对你来说无关紧要,因为插件还会自动更新 CSS 文件中的链接。一切都会正常工作!
Resources 插件本身就使静态资源的管理比以前简单得多。但这只是故事的一部分。由于插件知道所有资源并控制它们的链接生成,因此它可以执行额外的处理以添加有趣的行为。此处理由一个可扩展的映射器管道完成
不要将此图表视为绝对正确的参考:作为用户,确切的管道及其工作方式并不重要。关键是,如果你愿意,可以添加自己的映射器实现,或者只是安装一个提供自己映射器的插件。结果是,只需付出极少的努力,即可获得一些非常强大的功能。
例如,假设你安装了 Resources 插件,定义了一些模块,并且你的 GSP 视图和布局已设置为使用适当的标签。只需安装 Cached Resources 和 Zipped Resources 插件,你将立即开始满足一些 ySlow 建议,例如为所有静态资源设置长期 Expires 头和 gzip 压缩(尽管你可以禁用特定文件类型(如图像,它们通常已经压缩)的 gzip 压缩)。这不应该这么容易,对吗?但事实就是如此。
Resources 插件已经存在了一段时间,并且已经在一些生产站点中使用。它甚至在 Grails 网站上也在使用。这不足为奇:它将静态资源的改进管理与一个映射器管道相结合,该管道允许易于使用但功能强大的功能。经验丰富的 Web 开发人员会立即注意到差异,你可以期待未来会有越来越多的插件支持 Resources。它甚至有自己的用户指南。
由于该插件带来的优势,Grails 2 将其作为新 Grails 项目的默认插件,并将其集成到一些核心标签中,例如<g:resource>。但即使你还不能使用 Grails 2,你仍然可以将其与旧版本的 Grails 一起使用,并以很少的精力从出色的资源管理中受益。其关键设计原则之一是,它可以在任何 Grails 应用程序中安装而不会破坏任何东西。
无论你使用哪个版本的 Grails,安装此插件都将改善你作为 Web 开发人员的生活。这不容小觑。