JSF资源库的用途是什么,应该如何使用它?

JSF的<h:outputStylesheet><h:outputScript><h:outputScript>0组件有一个library属性。这是什么,该如何使用?在web上有很多例子,它们使用常见的内容/文件类型cssjsimg(或image)作为库名,具体取决于所使用的标记:

<h:outputStylesheet library="css" name="style.css" />
<h:outputScript library="js" name="script.js" />
<h:graphicImage library="img" name="logo.png" />

它有什么用?这些例子中的library值似乎只是重复标记名称所表示的内容。对于<h:outputStylesheet>,它基于标记名,很明显它代表了一个“CSS库”。这和下面的有什么区别呢?

<h:outputStylesheet name="css/style.css" />
<h:outputScript name="js/script.js" />
<h:graphicImage name="img/logo.png" />

此外,生成的HTML输出也略有不同。给定/contextnameFacesServlet的上下文路径映射到*.xhtml的URL模式上,前者以库名作为请求参数生成以下HTML:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/style.css.xhtml?ln=css" />
<script type="text/javascript" src="/contextname/javax.faces.resource/script.js.xhtml?ln=js"></script>
<img src="/contextname/javax.faces.resource/logo.png.xhtml?ln=img" alt="" />

而后者生成了下面的HTML,库名正好在URI的路径中:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml" alt="" />

事后看来,后一种方法也比前一种方法更有意义。library属性究竟是如何有用的?

131342 次浏览

实际上,在web上,所有那些常用的内容/文件类型,如“js”,“css”,“img”等被用作库名的例子都是误导

现实世界的例子

首先,让我们看看现有的JSF实现(如银鲈MyFaces)和JSF组件库(如PrimeFacesOmniFaces)是如何使用它的。它们中没有一个以这种方式使用资源库。他们使用它(在被子下,通过@ResourceDependencyUIViewRoot#addComponentResource())如下方式:

<h:outputScript library="javax.faces" name="jsf.js" />
<h:outputScript library="primefaces" name="jquery/jquery.js" />
<h:outputScript library="omnifaces" name="omnifaces.js" />
<h:outputScript library="omnifaces" name="fixviewstate.js" />
<h:outputScript library="omnifaces.combined" name="[dynamicname].js" />
<h:outputStylesheet library="primefaces" name="primefaces.css" />
<h:outputStylesheet library="primefaces-aristo" name="theme.css" />
<h:outputStylesheet library="primefaces-vader" name="theme.css" />

应该很清楚,它基本上表示所有这些资源通常属于的公共库/模块/主题名称

更容易识别

这样就更容易指定和区分这些资源属于和/或来自哪里。假设你刚好在你自己的webapp中有一个primefaces.css资源,其中你正在重写/微调PrimeFaces的一些默认CSS;如果PrimeFaces没有为它自己的primefaces.css使用库名,那么PrimeFaces自己的库名将不会被加载,而是webapp提供的库名,这将破坏观感。

此外,当你使用自定义ResourceHandler时,当正确使用library时,你还可以对来自特定库的资源应用更细粒度的控制。如果所有组件库的所有js文件都使用“js”,ResourceHandler如何区分它是否来自特定的组件库?例如OmniFaces CombinedResourceHandlerlibrary0;检查createResource()方法,其中库在委托给链中的下一个资源处理程序之前被检查。这样他们就知道什么时候该为此目的创建CombinedResourceGraphicResource

值得注意的是,RichFaces做错了。它根本没有使用任何library,而是在它上面自制了另一个资源处理层,因此不可能以编程方式识别RichFaces资源。这就是为什么OmniFaces CombinedResourceHander必须引入一个基于反射的hack的原因,以便让它在RichFaces资源中工作。

你自己的网络应用

你自己的web应用并不一定需要一个资源库。你最好把它省略掉。

<h:outputStylesheet name="css/style.css" />
<h:outputScript name="js/script.js" />
<h:graphicImage name="img/logo.png" />

或者,如果你真的需要一个,你可以给它一个更合理的常用名称,比如“default”或一些公司名称。

<h:outputStylesheet library="default" name="css/style.css" />
<h:outputScript library="default" name="js/script.js" />
<h:graphicImage library="default" name="img/logo.png" />

或者,当资源特定于某个主Facelets模板时,您还可以为其指定模板的名称,以便于相互关联。换句话说,这更多的是出于自我记录的目的。例如,在/WEB-INF/templates/layout.xhtml模板文件中:

<h:outputStylesheet library="layout" name="css/style.css" />
<h:outputScript library="layout" name="js/script.js" />

/WEB-INF/templates/admin.xhtml模板文件:

<h:outputStylesheet library="admin" name="css/style.css" />
<h:outputScript library="admin" name="js/script.js" />

对于一个真实的例子,检查OmniFaces展示源代码.;

或者,当你想在多个webapp上共享相同的资源,并基于这个答案中的相同示例为其创建了一个“公共”项目,该项目反过来作为JAR嵌入到webapp的/WEB-INF/lib中,然后也将其引用为库(名称由你选择自由;OmniFaces和PrimeFaces等组件库也是这样工作的):

<h:outputStylesheet library="common" name="css/style.css" />
<h:outputScript library="common" name="js/script.js" />
<h:graphicImage library="common" name="img/logo.png" />

库版本

另一个主要优势是,您可以对自己的webapp提供的资源应用资源库版本控制(这不适用于嵌入在JAR中的资源)。您可以在库文件夹中创建一个直接的子文件夹,其名称为\d+(_\d+)*模式,以表示资源库版本。

WebContent
|-- resources
|    `-- default
|         `-- 1_0
|              |-- css
|              |    `-- style.css
|              |-- img
|              |    `-- logo.png
|              `-- js
|                   `-- script.js
:

当使用这个标记时:

<h:outputStylesheet library="default" name="css/style.css" />
<h:outputScript library="default" name="js/script.js" />
<h:graphicImage library="default" name="img/logo.png" />

这将生成以下HTML,库版本为v参数:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml?ln=default&amp;v=1_0" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&amp;v=1_0"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&amp;v=1_0" alt="" />

因此,如果您编辑/更新了一些资源,那么您所需要做的就是将版本文件夹复制或重命名为一个新值。如果你有多个版本文件夹,那么JSF ResourceHandler将根据数值排序规则自动从最高的版本号提供资源。

因此,当将resources/default/1_0/*文件夹复制/重命名到resources/default/1_1/*时,如下所示:

WebContent
|-- resources
|    `-- default
|         |-- 1_0
|         |    :
|         |
|         `-- 1_1
|              |-- css
|              |    `-- style.css
|              |-- img
|              |    `-- logo.png
|              `-- js
|                   `-- script.js
:

然后最后一个标记示例将生成以下HTML:

<link rel="stylesheet" type="text/css" href="/contextname/javax.faces.resource/css/style.css.xhtml?ln=default&amp;v=1_1" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&amp;v=1_1"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&amp;v=1_1" alt="" />

这将迫使web浏览器直接从服务器请求资源,而不是从缓存中显示具有相同名称的资源,当参数发生变化的URL第一次被请求时。这样,当最终用户需要检索更新的CSS/JS资源时,就不需要执行硬刷新(Ctrl+F5等)。

请注意,对于JAR文件中包含的资源,库版本控制是不可能的。你需要一个自定义ResourceHandler。另见如何在jar中使用JSF版本控制资源

参见: