6.2.5 静态资源 - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: null
6.2.5 静态资源
Grails 2.0 integrates with the Resources plugin to provide sophisticated static resource management. This plugin is installed by default in new Grails applications.The basic way to include a link to a static resource in your application is to use the resource tag. This simple approach creates a URI pointing to the file.However modern applications with dependencies on multiple JavaScript and CSS libraries and frameworks (as well as dependencies on multiple Grails plugins) require something more powerful.The issues that the Resources framework tackles are:
Grails 2.0集成了资源插件以提供更复杂的静态资源管理。此插件在新建Grails应用中是缺省安装的。在你的应用中要引用一个静态资源链接的基本方法,就是使用resource标签。此种方式会创建一个指向文件的URI。但是,现在的应用往往会依赖于多个JavaScript、CSS库和框架(即依赖于多个Grails插件),这就要求一些更强大的功能来支撑。本资源(Resources)框架要解决的主要问题如下:
- Web application performance tuning is difficult
- Correct ordering of resources, and deferred inclusion of JavaScript
- Resources that depend on others that must be loaded first
- The need for a standard way to expose static resources in plugins and applications
- The need for an extensible processing chain to optimize resources
- Preventing multiple inclusion of the same resource
- Web应用的性能调优是非常困难的
- 正确地对资源排序,推迟引用JavaScript
- 依赖于其他的资源必须要优先加载
- 在插件和应用中需要采用一种标准的方式来暴露静态资源
- 需要扩展性更好的处理链来优化资源
- 阻止同样的资源被多次引用
6.2.5.1 通过资源标签引用资源
Pulling in resources with r:require
To use resources, your GSP page must indicate which resource modules it requires. For example with the jQuery plugin, which exposes a "jquery" resource module, to use jQuery in any page on your site you simply add:<html> <head> <r:require module="jquery"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
r:require multiple times in a GSP page, and you use the "modules" attribute to provide a list of modules:<html> <head> <r:require modules="jquery, main, blueprint, charting"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
使用r:require获取资源
要使用资源,你的GSP页面必须要知道那些资源模块是所需要的。以jQuery插件为例,其导出了一个"jquery"的资源模块,要在你站点的任何页面使用jQuery,你需要简单地增加如下代码:<html> <head> <r:require module="jquery"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
r:require,也可以使用"modules"属性提供一个模块列表:<html> <head> <r:require modules="jquery, main, blueprint, charting"/> <r:layoutResources/> </head> <body> … <r:layoutResources/> </body> </html>
Rendering the links to resources with r:layoutResources
When you have declared the resource modules that your GSP page requires, the framework needs to render the links to those resources at the correct time.To achieve this correctly, you must include the r:layoutResources tag twice in your page, or more commonly, in your GSP layout:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
使用r:layoutResources渲染资源链接
当在你的GSP页面中声明所需要的资源模块时,插件框架需要在正确的时间渲染那些资源的链接。要正确地处理,你必须在你的页面中引用两次r:layoutResources标签,或者更通用的方式是在你的GSP布局中处理:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
Adding page-specific JavaScript code with r:script
Grails has the javascript tag which is adapted to defer to Resources plugin if installed, but it is recommended that you callr:script directly when you need to include fragments of JavaScript code.This lets you write some "inline" JavaScript which is actually not rendered inline, but either in the <head> or at the end of the body, based on the disposition.Given a Sitemesh layout like this:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
<html> <head> <title>Testing r:script magic!</title> </head> <body> <r:script disposition="head"> window.alert('This is at the end of <head>'); </r:script> <r:script disposition="defer"> window.alert('This is at the end of the body, and the page has loaded.'); </r:script> </body> </html>
使用r:script增加特定页面的JavaScript代码
在资源插件安装以后,Grails的javascript标签将被适配到优先使用资源插件,即便如此,如果你需要直接使用JavaScript代码片段,还是推荐你直接调用r:script。这可以让你写一些“内联(inline)”的JavaScript,但实际 不在 内联时渲染,而是根据其安排,决定是在<head>或者body的结尾。假设一个Sitemesh布局如下所示:<html> <head> <g:layoutTitle/> <r:layoutResources/> </head> <body> <g:layoutBody/> <r:layoutResources/> </body> </html>
<html> <head> <title>Testing r:script magic!</title> </head> <body> <r:script disposition="head"> window.alert('This is at the end of <head>'); </r:script> <r:script disposition="defer"> window.alert('This is at the end of the body, and the page has loaded.'); </r:script> </body> </html>
Linking to images with r:img
This tag is used to render<img> markup, using the Resources framework to process the resource on the fly (if configured to do so - e.g. make it eternally cacheable).This includes any extra attributes on the <img> tag if the resource has been previously declared in a module.With this mechanism you can specify the width, height and any other attributes in the resource declaration in the module, and they will be pulled in as necessary.Example:<html> <head> <title>Testing r:img</title> </head> <body> <r:img uri="/images/logo.png"/> </body> </html>
g:img tag as a shortcut for rendering <img> tags that refer to a static resource. The Grails img tag is Resources-aware and will delegate to r:img if found. However it is recommended that you use r:img directly if using the Resources plugin.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.
使用r:img链接图片
此标签被用以渲染HTML的<img>标签,并且通过资源框架来处理那些频繁访问的资源(如果配置了的话,比如使其永久的缓存)。如果资源在以前已经被声明为一个模块的话,那么r:img会包含<img>标签的任何额外属性。基于此机制,你可以在声明资源模块的时候,来指定width、height以及其他任何属性,然后在需要的时候获取一下即可。比如:<html> <head> <title>Testing r:img</title> </head> <body> <r:img uri="/images/logo.png"/> </body> </html>
g:img标签只是渲染静态资源<img>的一个快捷方式而已。Grails的img的标签如果感知到资源插件,那么将会将其代理给r:img。即便如此,如果使用了资源插件的话,还是推荐直接你使用r:img。跟常规的Grails的resource标签属性一样,为了增加简洁性,r:img也支撑"uri"属性。更多完整的详细信息请参考r:resource文档。
6.2.5.2 其他资源标签
r:resource
This is equivalent to the Grails resource tag, returning a link to the processed static resource. Grails' owng:resource tag delegates to this implementation if found, but if your code requires the Resources plugin, you should use r:resource directly.Alongside the regular Grails resource tag attributes, this also supports the "uri" attribute for increased brevity.See r:resource documentation for full details.
r:resource
这跟Grails的resource标签相当,都是返回一个处理过的静态资源链接。如果发现资源插件已经安装,Grails自带的g:resource标签将代理给r:resource,但是如果你的代码依赖资源插件,最好还是直接使用r:resource的好。跟常规的Grails的resource标签属性一样,为了增加简洁性,r:resource也支撑"uri"属性。更多完整的详细信息请参考r:resource文档。r:external
This is a resource-aware version of Grails external tag which renders the HTML markup necessary to include an external file resource such as CSS, JS or a favicon.See r:resource documentation for full details.r:external
这是一个资源感知(resource-aware)版本的Grails的external标签,用以渲染那些必要的HTML标签所需要的外部资源文件,比如CSS、JS或者favicon。更多完整的详细信息请参考r:resource文档。6.2.5.3 声明资源
A DSL is provided for declaring resources and modules. This can go either in your This defines three resource modules; 'core', 'utils' and 'forms'. The resources in these modules will be automatically bundled out of the box according to the module name, resulting in fewer files. You can override this with
系统提供了一个DSL专门用于声明资源和模块。其可以位于Config.groovy in the case of application-specific resources, or more commonly in a resources artefact in grails-app/conf.Note that you do not need to declare all your static resources, especially images. However you must to establish dependencies or other resources-specific attributes. Any resource that is not declared is called "ad-hoc" and will still be processed using defaults for that resource type.Consider this example resource configuration file, grails-app/conf/MyAppResources.groovy:modules = {
core {
dependsOn 'jquery, utils' 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'
}
}bundle:'someOtherName' on each resource, or call defaultBundle on the module (see resources plugin documentation).It declares dependencies between them using dependsOn, which controls the load order of the resources.When you include an <r:require module="forms"/> in your GSP, it will pull in all the resources from 'core' and 'utils' as well as 'jquery', all in the correct order.You'll also notice the disposition:'head' on the core.js file. This tells Resources that while it can defer all the other JS files to the end of the body, this one must go into the <head>.The CSS file for print styling adds custom attributes using the attrs map option, and these are passed through to the r:external tag when the engine renders the link to the resource, so you can customize the HTML attributes of the generated link.There is no limit to the number of modules or xxxResources.groovy artefacts you can provide, and plugins can supply them to expose modules to applications, which is exactly how the jQuery plugin works.To define modules like this in your application's Config.groovy, you simply assign the DSL closure to the grails.resources.modules Config variable.For full details of the resource DSL please see the resources plugin documentation.
Config.groovy中特定应用的资源容器,或者更常用的是grails-app/conf下的一个资源工件中。注意!你并不需要声明所有的静态资源,尤其是图片。但是你必须要建立所需依赖或者其他资源相关的属性。没有被声明的资源称之为"ad-hoc",并且根据其资源类型被缺省处理。假设如下所示的grails-app/conf/MyAppResources.groovy资源配置文件:modules = {
core {
dependsOn 'jquery, utils' 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'
}
}bundle:'someOtherName' 来覆盖之,或者调用模块的defaultBundle(更多请参考资源插件文档)。通过dependsOn来声明的依赖关系,可以控制资源的加载顺序。当你在的GSP中引用<r:require module="forms"/>的时候,它将从'core'和'utils'还有'jquery'中获取所有的资源,并以正确的顺序加载。你将会注意到core.js文件中的disposition:'head' 。它将告诉资源插件当所有的其他JS文件推迟到body末尾加载的时候,此文件(core.js)必须要在<head>加载。用于打印风格的CSS文件通过attrs映射选项来添加自定义属性,并且在渲染到资源链接的时候,它们将被传递给r:external标签,因此你可以自定义HTML的属性来生成链接。模块或者你定义的xxxResources.groovy工件的数量是没有限制的,插件也可以将资源模块暴露给应用,正如jQuery插件所做的那样。要在你应用中的Config.groovy定义模块,你可以简单地将DSL闭包赋给grails.resources.modules配置变量。完整的资源DSL信息请参考资源插件文档。
6.2.5.4 覆盖插件资源
Because a resource module can define the bundle groupings and other attributes of resources, you may find that the settings provided are not correct for your application.For example, you may wish to bundle jQuery and some other libraries all together in one file. There is a load-time and caching trade-off here, but often it is the case that you'd like to override some of these settings.To do this, the DSL supports an "overrides" clause, within which you can change the This will put all code into a single bundle named 'monolith'. Note that this can still result in multiple files, as separate bundles are required for head and defer dispositions, and JavaScript and CSS files are bundled separately.Note that overriding individual resources requires the original declaration to have included a unique id for the resource.For full details of the resource DSL please see the resources plugin documentation.
因为一个资源模块定义了捆绑(bundle)组和资源的其他属性,因此你可能会发现设置所提供的并不适合你的应用。比如,你可能希望将jQuery和其他的库捆绑到一个文件中。此处就要根据加载时间和缓存做一个权衡,但是在此种情况下,你经常会想重载这些配置的一部分。这时候,DSL提供了"overrides"子句来完成此功能,子句内你可以修改一个模块的defaultBundle setting for a module, or attributes of individual resources that have been declared with a unique id:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'monolith' 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'
defaultBundle 'monolith' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils'
defaultBundle 'monolith' resource url: '/css/forms.css'
resource url: '/js/forms.js'
} overrides {
jquery {
defaultBundle 'monolith'
}
}
}defaultBundle,或者每个单独资源的属性,不过每个资源必须要声明一个唯一的id:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'monolith' 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'
defaultBundle 'monolith' resource url: '/js/utils.js'
} forms {
dependsOn 'core,utils'
defaultBundle 'monolith' resource url: '/css/forms.css'
resource url: '/js/forms.js'
} overrides {
jquery {
defaultBundle 'monolith'
}
}
}6.2.5.5 优化资源
The Resources framework uses "mappers" to mutate the resources into the final format served to the user.The resource mappers are applied to each static resource once, in a specific order. You can create your own resource mappers, and several plugins provide some already for zipping, caching and minifying.Out of the box, the Resources plugin provides bundling of resources into fewer files, which is achieved with a few mappers that also perform CSS re-writing to handle when your CSS files are moved into a bundle.
资源框架使用"映射器(mappers)"来将资源转变为最终用户所需的格式。资源映射器以一个特定的顺序将每一个静态资源处理一次。你可以创建你自己的资源映射器,有一些插件已经提供了比如压缩(zipping)、缓存(caching)和缩少(minifying)等映射。除此之外,资源插件还提供了捆绑多个资源到较少的文件功能,在将你的CSS文件移动到一个捆绑束的时候,其使用一些映射器执行重写CSS处理。Bundling multiple resources into fewer files
The 'bundle' mapper operates by default on any resource with a "bundle" defined - or inherited from adefaultBundle clause on the module. Modules have an implicit default bundle name the same as the name of the module.Files of the same kind will be aggregated into this bundle file. Bundles operate across module boundaries:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'common' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js', bundle: 'ui'
resource url: '/css/main.css', bundle: 'theme'
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js', bundle: 'common'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css', bundle: 'ui'
resource url: '/js/forms.js', bundle: 'ui'
}
}捆绑多个资源到较少的文件
缺省情况下,'bundle'映射器会操作使用"bundle"定义的任何资源-或者继承自模块的defaultBundle子句。模块有一个隐含的跟模块名称相同的缺省捆绑束名。同样类型的文件将会被汇集到当前的捆绑束文件中。捆绑束是通过模块的边界来操作的:modules = {
core {
dependsOn 'jquery, utils'
defaultBundle 'common' resource url: '/js/core.js', disposition: 'head'
resource url: '/js/ui.js', bundle: 'ui'
resource url: '/css/main.css', bundle: 'theme'
resource url: '/css/branding.css'
resource url: '/css/print.css', attrs: [media: 'print']
} utils {
dependsOn 'jquery' resource url: '/js/utils.js', bundle: 'common'
} forms {
dependsOn 'core,utils' resource url: '/css/forms.css', bundle: 'ui'
resource url: '/js/forms.js', bundle: 'ui'
}
}Making resources cache "eternally" in the client browser
Caching resources "eternally" in the client is only viable if the resource has a unique name that changes whenever the contents change, and requires caching headers to be set on the response.The cached-resources plugin provides a mapper that achieves this by hashing your files and renaming them based on this hash. It also sets the caching headers on every response for those resources. To use, simply install the cached-resources plugin.Note that the caching headers can only be set if your resources are being served by your application. If you have another server serving the static content from your app (e.g. Apache HTTPD), configure it to send caching headers. Alternatively you can configure it to request and proxy the resources from your container.让资源“永久”地缓存在客户浏览器
在客户端“永久”地缓存资源只有在资源有一个唯一的名字的情况下,才切实可行,并且当资源的内容变化时,其名字也要做相应的变化,还要求在响应中设置缓存标头(caching headers)。cached-resources插件提供了一个映射器来完成此功能,它是通过对你的文件做哈稀校验并根据校验值来重命名来实现的。此插件也会在每一次的响应中根据这些资源的情况来设置缓存标头。要使用它,只需要简单的安装cached-resources插件即可。注意!只有在你的应用管辖范围内的资源,才会有可能设置缓存标头.如果你有另外一个服务器专门管理你应用的静态资源(比如Apache的HTTPD),那么需要配置此服务器来发送缓存标头。或者你也可以配置它来请求和代理你容器内的资源。Zipping resources
Returning gzipped resources is another way to reduce page load times and reduce bandwidth.The zipped-resources plugin provides a mapper that automatically compresses your content, excluding by default already compressed formats such as gif, jpeg and png.Simply install the zipped-resources plugin and it works.压缩资源
返回用gzip压缩过的资源是另外减少页面加载时间和带宽的方法。zipped-resources插件提供了一个映射器来自动地压缩你的资源内容。当然那些已经压缩过地除外,比如gif、jpeg和png。简单地安装zipped-resources插件后,即可工作。Minifying
There are a number of CSS and JavaScript minifiers available to obfuscate and reduce the size of your code. At the time of writing none are publicly released but releases are imminent.缩少资源
已经有很多的CSS和JavaScript缩少器可以用来混淆和减少你代码的大小。这可以解决发布迫在眉睫,而现在编码时没有什么可公开发布的情况。6.2.5.6 调试
When your resources are being moved around, renamed and otherwise mutated, it can be hard to debug client-side issues. Modern browsers, especially Safari, Chrome and Firefox have excellent tools that let you view all the resources requested by a page, including the headers and other information about them.There are several debugging features built in to the Resources framework.
当你的资源正在移动、重命名以及其他变动的时候,要调试客户端的问题是非常困难的。现代的浏览器,尤其是Safari、Chrome和Firefox,都有非常优秀的工具来查看一个请求页面的所有资源,包括其请求头和其他的信息。除此之外,Resources框架还提供了几个内置的调试特性。X-Grails-Resources-Original-Src Header
Every resource served in development mode will have the X-Grails-Resources-Original-Src: header added, indicating the original source file(s) that make up the response.X-Grails-Resources-Original-Src信息头
在开发模式下,每一个用到的资源都会添加X-Grails-Resources-Original-Src的头信息,用以表示此响应对应的原始代码文件。Adding the debug flag
If you add a query parameter _debugResources=y to your URL and request the page, Resources will bypass any processing so that you can see your original source files.This also adds a unique timestamp to all your resource URLs, to defeat any caching that browsers may use. This means that you should always see your very latest code when you reload the page.添加调试标记
如果在你的URL和请求页面的参数中增加 _debugResources=y 的话,Resources将会不管任何处理,而直接显示和使用原始的代码文件。此外,你资源的URLs还会添加一个唯一的时间戳,用以处理浏览器导致的缓存问题。这意味着在你重现加载页面的时候,你总是得到最新的代码。Turning on debug all the time
You can turn on the aforementioned debug mechanism without requiring a query parameter, but turning it on in Config.groovy:grails.resources.debug = true打开调试
你可以在不需要额外请求参数的情况下打开如上所述的调试机制,要如此,只要在Config.groovy中配置一下即可:grails.resources.debug = true6.2.5.7 阻止资源处理
Sometimes you do not want a resource to be processed in a particular way, or even at all. Occasionally you may also want to disable all resource mapping.
有时候,你并不想以一种特别的方式处理资源,甚至根本就不想。偶尔,你还想禁止所有的资源映射。Preventing the application of a specific mapper to an individual resource
All resource declarations support a convention of noXXXX:true where XXXX is a mapper name.So for example to prevent the "hashandcache" mapper from being applied to a resource (which renames and moves it, potentially breaking relative links written in JavaScript code), you would do this:modules = {
forms {
resource url: '/css/forms.css', nohashandcache: true
resource url: '/js/forms.js', nohashandcache: true
}
}阻止到一个单独资源的特定映射
所有的资源声明都支持noXXXX:true的用法,此处的XXXX是一个映射器的名称。因此在下例中,要阻止"hashandcache"映射器应用到一个资源(重命名,移动甚至断开JavaScript代码中的相关链接)你可以这样做:modules = {
forms {
resource url: '/css/forms.css', nohashandcache: true
resource url: '/js/forms.js', nohashandcache: true
}
}Excluding/including paths and file types from specific mappers
Mappers have includes/excludes Ant patterns to control whether they apply to a given resource. Mappers set sensible defaults for these based on their activity, for example the zipped-resources plugin's "zip" mapper is set to exclude images by default.You can configure this in yourConfig.groovy using the mapper name e.g:// We wouldn't link to .exe files using Resources but for the sake of example: grails.resources.zip.excludes = ['**/*.zip', '**/*.exe']// Perhaps for some reason we want to prevent bundling on "less" CSS files: grails.resources.bundle.excludes = ['**/*.less']
从特定映射器中 排除/包含 路径和文件类型
映射器的排除/包含使用Ant语法来控制是否要应用到给定的资源上。映射器会根据其活动情况来设置缺省的感知类型,以资源压缩(zipped-resources)插件为例,其"zip"映射器会缺省地排除那些镜像文件。你可以通过你的Config.groovy文件地映射器名称来配置相关信息,比如:// We wouldn't link to .exe files using Resources but for the sake of example: grails.resources.zip.excludes = ['**/*.zip', '**/*.exe']// Perhaps for some reason we want to prevent bundling on "less" CSS files: grails.resources.bundle.excludes = ['**/*.less']
Controlling what is treated as an "ad-hoc" (legacy) resource
Ad-hoc resources are those undeclared, but linked to directly in your application without using the Grails or Resources linking tags (resource, img or external).These may occur with some legacy plugins or code with hardcoded paths in.There is a Config.groovy setting grails.resources.adhoc.patterns which defines a list of Servlet API compliant filter URI mappings, which the Resources filter will use to detect such "ad-hoc resource" requests.By default this is set to:grails.resources.adhoc.patterns = ['images/*', '*.js', '*.css']
控制"ad-hoc"(遗留)资源
Ad-hoc资源是那些未声明的,并且 不 使用Grails或者Resources的链接标签(resource, img or external),而是在你的应用中直接链接的资源。这可能会在那些遗留插件或者硬编码路径的时候会碰到。Config.groovy中的 grails.resources.adhoc.patterns 配置就是用来定义一系列Servlet API兼容的URI映射的过滤器,其资源过滤器通常用来检测那些"ad-hoc resource"请求。其缺省值如下:grails.resources.adhoc.patterns = ['images/*', '*.js', '*.css']
6.2.5.8 其他资源感知的插件
At the time of writing, the following plugins include support for the Resources framework:
截至到书写为止,资源框架已经被下列插件所支撑:

