JSF资源库是用来做什么的?如何使用?

239

JSF <h:outputStylesheet>, <h:outputScript><h:graphicImage>组件都有一个library属性。这是什么意思,应该如何使用?网络上有很多示例,使用常见的内容/文件类型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 属性到底有什么用处呢?
1个回答

268

实际上,网络上所有将常见的内容/文件类型如“js”、“css”、“img”等用作库名称的例子都是误导性的

现实世界中的例子

首先,让我们看看现有的JSF实现(例如MojarraMyFaces)以及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" />

很明显,它基本上代表了所有这些资源通常属于的公共库/模块/主题名称。

更易于识别

这样做可以更轻松地指定和区分资源所属和/或来自哪里。想象一下,你在自己的Web应用程序中有一个primefaces.css资源,在其中覆盖/微调PrimeFaces的默认CSS;如果PrimeFaces没有为其自己的primefaces.css使用库名称,那么不会加载PrimeFaces自己的文件,而是加载Web应用程序提供的文件,从而破坏外观和感觉。

此外,当您使用自定义ResourceHandler时,如果正确使用library,还可以对来自特定库的资源应用更精细化的控制。如果所有组件库都使用"js"作为其所有JS文件的库名称,那么ResourceHandler怎样才能区分它是否来自特定的组件库呢?这些示例包括OmniFaces的CombinedResourceHandlerGraphicResourceHandler;请参阅createResource()方法,其中检查了库以便在链中委托给下一个资源处理程序之前进行处理。这样他们就知道何时为特定的目的创建CombinedResourceGraphicResource

需要注意的是,RichFaces做错了。它根本没有使用任何library,而是自制了另一种资源处理层,因此无法以编程方式识别RichFaces资源。这正是OmniFaces CombinedResourceHander不得不引入反射式黑客的原因,以使其可以与RichFaces资源一起正常工作。

您自己的Web应用程序

您自己的Web应用程序不一定需要资源库。最好省略它。

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

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

<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展示源代码

或者,当你想要在多个web应用程序之间共享相同资源并已经创建了一个基于与这个答案中相同示例的“common”项目时,将其作为库引用(名称可自由选择;组件库如OmniFaces和PrimeFaces也是这样做的):将其嵌入到web应用程序的/WEB-INF/lib中。

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

库版本控制

另一个主要优势是,您可以正确地对自己Web应用程序提供的资源库应用资源库版本控制(这对于嵌入在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" />

使用库版本作为v参数将生成以下HTML:

<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="" />

当URL首次请求带有更改参数的资源时,这将强制网络浏览器直接从服务器请求资源,而不是显示具有相同名称的缓存中的资源。这样,当需要检索更新的CSS / JS资源时,最终用户无需进行硬刷新(Ctrl + F5等)。

请注意,对于嵌入JAR文件中的资源,无法进行库版本控制,您需要使用自定义ResourceHandler。请参见How to use JSF versioning for resources in jar

另请参阅:


2
能否使用EL作为库?因此,如果我想要有一个resources/default和一个resources/feelingFroggyToday,我可以做类似library="#{someLibraryHere}"的事情,将someLibraryHere映射到我选择的库,而不必每次想要更改它们时都依赖于将资源目录重命名为更高版本。 - gebuh
嗯,非常有趣的Balus。我在一个Web应用程序中遇到了一个问题,即在加载时主题.css文件为空。这只发生在多次重新部署之后(在JBOSS EAP中)。CSS URL如下:/javax.faces.resource/css/theme.css.xhtml?ln=default&v=3_3_0_130416,并且声明方式如下:<h:outputStylesheet library="default" name="css/theme.css" target="head"/>。也许这个问题与版本控制有关? - Ricardo Vila
2
library 的允许字符或与其相关的内容在 mojarra 2.2.5(2.2.5-jbossorg-3,wildfly 8.0)和 2.2.11(2.2.11-jbossorg-1)之间是否有更改?我似乎在发布说明中找不到任何信息。请参见https://dev59.com/qpTfa4cB1Zd3GeqPTJxd。 - Kukeltje
3
感谢@BalusC。不幸的是,即使是Oracle自己的Java EE 7教程在第8.6章[Web资源]中也使用了库名css的错误示例,并在[guessnumber-jsf示例应用程序]中处理css和图像时出现问题。 - Jesper
1
@Arash:不需要将当前语言环境复制粘贴到整个库中,只需按照您找到的链接中的说明调整文件夹结构即可,其余部分都是自动完成的。 - BalusC
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接