JSF <h:outputStylesheet>
、<h:outputScript>
和 <h:graphicImage>
组件具有 library
属性。这是什么,应该如何使用?网络上有很多示例,它们使用常见的内容/文件类型 css
、js
和 img
(或 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 输出有点不同。给定 /contextname
的上下文路径和 *.xhtml
的 URL 模式上的 FacesServlet
映射,前者生成以下 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="" />
而后者在 URI 的路径中生成以下带有库名称的 HTML:
<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
属性究竟有多大用处?
实际上,网络上的所有这些示例,其中使用“js”、“css”、“img”等常见内容/文件类型作为库名称都是误导性的。
现实世界的例子
首先,让我们看看现有的 JSF 实现(如 Mojarra 和 MyFaces)以及 JSF 组件库(如 PrimeFaces 和 OmniFaces)如何使用它。他们中没有人以这种方式使用资源库。他们使用它(在幕后,通过 @ResourceDependency
或 UIViewRoot#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 自己的库名称,而是加载 webapp 提供的库名称,这会破坏外观。
此外,当您使用自定义 ResourceHandler
时,如果正确使用 library
,您还可以对来自特定库的资源应用更精细的控制。如果所有组件库都对其所有 JS 文件使用“js”,ResourceHandler
将如何区分它是否来自特定组件库?例如 OmniFaces CombinedResourceHandler
和 GraphicResourceHandler
;检查 createResource()
方法,其中在委派给链中的下一个资源处理程序之前检查库。这样他们就知道何时为此目的创建 CombinedResource
或 GraphicResource
。
应该注意的是 RichFaces 做错了。它根本没有使用任何 library
并且在其上自制了另一个资源处理层,因此不可能以编程方式识别 RichFaces 资源。这正是 OmniFaces CombinedResourceHander
必须引入 a reflection-based hack 才能使其与 RichFaces 资源一起使用的原因。
你自己的网络应用
你自己的 webapp 不一定需要资源库。你最好忽略它。
<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 showcase source code。
或者,如果您想在多个 web 应用程序上共享相同的资源,并基于与 this answer 中相同的示例创建了一个“通用”项目,该项目又作为 JAR 嵌入到 web 应用程序的 /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 中的资源)。您可以在 library 文件夹中使用 \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&v=1_0" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&v=1_0"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&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&v=1_1" />
<script type="text/javascript" src="/contextname/javax.faces.resource/js/script.js.xhtml?ln=default&v=1_1"></script>
<img src="/contextname/javax.faces.resource/img/logo.png.xhtml?ln=default&v=1_1" alt="" />
当第一次请求具有更改参数的 URL 时,这将强制 Web 浏览器直接从服务器请求资源,而不是从缓存中显示具有相同名称的资源。这样,最终用户在需要检索更新的 CSS/JS 资源时不需要进行硬刷新(Ctrl+F5 等)。
请注意,对于包含在 JAR 文件中的资源,库版本控制是不可能的。您需要一个自定义 ResourceHandler
。另见How to use JSF versioning for resources in jar。
也可以看看:
JSF 资源版本控制
JSF2 静态资源缓存
具有共享代码的多个 JSF 项目的结构
JSF 2.0 规范 - 第 2.6 章资源处理
不定期副业成功案例分享
library
值或与之相关的字符是否发生了变化?我似乎在发行说明中找不到任何东西。请参阅stackoverflow.com/questions/35719808/…css
并在 guessnumber-jsf example application 中使用 css 和图像时也给出了错误的示例。