如何在Facelets模板中引用CSS / JS / 图像资源?

64

我已经完成了有关Facelets模板的教程。

现在我尝试创建一个不在同一目录中的页面。由于样式是使用相对路径引用的,所以我遇到了样式问题,如下所示:

样式问题需要进一步说明

<link rel="stylesheet" href="style_resource_path.css" />

我可以使用绝对引用,只需从/开始:

<link rel="stylesheet" href="/project_root_path/style_resource_path.css" />

但是当我将应用程序移植到不同的环境时,这会给我带来麻烦。

所以我想知道在Facelets中引用CSS(JS和图像)资源的最佳方式是什么?

4个回答

133

介绍

JSF 2.x 的正确方式是使用 <h:outputStylesheet>, <h:outputScript><h:graphicImage>,并使用相对于 webapp 的 /resources 文件夹的路径引用 name。这样,您就不需要像在 JSF 1.x 中那样担心上下文路径。另请参见如何在 JSF 1.x 中相对于上下文路径包含 CSS?

文件夹结构

将 CSS/JS/图像文件放入公共 Web 内容的 /resources 文件夹中,如下所示(如果与 /WEB-INF/META-INF 处于同一级别,则只需创建一个文件夹即可)。

WebContent
 |-- resources
 |    |-- css
 |    |    |-- other.css
 |    |    `-- style.css
 |    |-- js
 |    |    `-- script.js
 |    `-- images
 |         |-- background.png
 |         |-- favicon.ico
 |         `-- logo.png
 |-- META-INF
 |    `-- MANIFEST.MF
 |-- WEB-INF
 |    |-- faces-config.xml
 |    `-- web.xml
 |-- page.xhtml
 :

在Maven中,应该放在/main/webapp/resources而不是/main/resources(后者是Java资源(属性/XML/文本/配置文件),必须最终出现在运行时类路径中,而不是Web内容)。另请参见Maven和JSF Web应用程序结构,在哪里放置JSF资源

在Facelets中引用

最终,这些资源将在任何地方都可以使用,无需处理相对路径。
<h:head>
    ...
    <h:outputStylesheet name="css/style.css" />
    <h:outputScript name="js/script.js" />
</h:head>
<h:body>
    ...
    <h:graphicImage name="images/logo.png" />
    ...
</h:body>
name属性必须表示相对于/resources文件夹的完整路径。它不需要以/开头。只要您不是开发像PrimeFaces这样的组件库或被多个Web应用程序共享的公共模块JAR文件,就不需要library属性。

您可以在任何地方引用<h:outputStylesheet>,也可以在模板客户端的<ui:define>中引用,无需额外的<h:head>。它将通过主模板的<h:head>组件自动出现在生成的<head>中。

<ui:define name="...">
    <h:outputStylesheet name="css/style.css" />
    ...
</ui:define>

你可以在任何地方引用<h:outputScript>,但默认情况下它会以你声明的位置直接出现在HTML中。如果你希望它通过<h:head>最终出现在<head>中,则需添加target="head"属性。
<ui:define name="...">
    <h:outputScript name="js/script.js" target="head" />
    ...
</ui:define>

或者,如果你希望它最终出现在<body>的结尾(就在</body>之前,这样例如window.onload$(document).ready()等就不是必需的),通过<h:body>,那么添加target="body"属性。

<ui:define name="...">
    <h:outputScript name="js/script.js" target="body" />
    ...
</ui:define>

PrimeFaces HeadRenderer

如果你在使用PrimeFaces,它的HeadRenderer会破坏默认的<h:head>脚本顺序,如上所述。你基本上需要通过PrimeFaces特定的<f:facet name="first|middle|last">来强制排序,这可能会导致混乱和难以模板化的代码。你可以像this answer中描述的那样关闭它。

JAR包装

你甚至可以将资源打包到JAR文件中。参见Structure for multiple JSF projects with shared code

EL引用

你可以在EL中使用#{resource}映射,让JSF基本上打印出一个资源URL,例如/context/javax.faces.resource/folder/file.ext.xhtml?ln=library,这样你就可以将其用作CSS背景图像或favicon。唯一的要求是CSS文件本身也应该作为JSF资源提供,否则EL表达式将不会被评估。参见How to reference JSF image resource as CSS background image url

.some {
    background-image: url("#{resource['images/background.png']}");
}

这是一个@import的例子。
@import url("#{resource['css/other.css']}");

这是一个网站图标的示例。还可以查看将网站图标添加到JSF项目中,并在<link>中引用它
<link rel="shortcut icon" href="#{resource['images/favicon.ico']}" />

如果您使用SCSS编译器(例如Maven的Sass编译器插件),请注意SCSS处理器可能将#解释为特殊字符。在这种情况下,您需要使用\进行转义。

.some {
    background-image: url("\#{resource['images/background.png']}");
}

引用第三方CSS文件

通过<h:outputStylesheet>加载的第三方CSS文件,如果引用了字体和/或图像,则可能需要更改为使用先前部分中描述的#{resource}表达式,否则需要安装UnmappedResourceHandler以便能够使用JSF提供服务。另请参见Bootsfaces页面在浏览器中没有任何样式如何使用Font Awesome 4.x CSS文件与JSF?浏览器找不到字体文件

隐藏在/WEB-INF中

如果您打算通过将整个/resources文件夹移动到/WEB-INF来隐藏资源以防止公共访问,则可以自JSF 2.2起通过新的web.xml上下文参数选择更改Web内容相关路径,方法如下:

<context-param>
    <param-name>javax.faces.WEBAPP_RESOURCES_DIRECTORY</param-name>
    <param-value>/WEB-INF/resources</param-value>
</context-param>

在旧版的JSF中,这是不可能的。
另请参阅:

我已经在CSS中使其工作,但如果我想在JavaScript中引用文件,该怎么办?在CSS文件中,符号url("#{resource['otbofaces:date-entry/calendar.gif']}")非常有效,但我还没有找到任何可以在javascript文件中使用以获取正确资源位置的内容。 - mrswadge
我所缺少的是我原本期望'template.xhtml'也是一种资源。因此,我完全预期执行类似于<ui:composition template="/templates/report.xhtml">这样的操作,其中我的模板驻留在web/resources/templates/report.xhtml,甚至利用库中的资源结构。但事实并非如此。 - YoYo
感谢有用的文档 - GyroCocoa
1
@Arash:只要您继续使用h:outputScript/h:outputStylesheet,就没有关系。或者如果您实际上想问“是否可以使用script/link代替h:outputScript/h:outputStylesheet?”,那么不推荐这样做,因为您将有可能出现重复的潜在问题,并且无法通过自定义的“ResourceHandler”进行控制。 - BalusC
谢谢和感谢您的出色答案。 - Arash
显示剩余7条评论

7
假设您正在运行Web应用程序的子目录中。您可以尝试以下操作:
 <link href="${facesContext.externalContext.requestContextPath}/css/style.css" rel="stylesheet" type="text/css"/>

该链接'${facesContext.externalContext.requestContextPath}/' 可以帮助您立即返回上下文的根目录。

在相对URL中,前导斜杠/指向域根。因此,如果JSF页面例如由http://example.com/context/page.jsf请求,则CSS URL将绝对指向http://example.com/styles/decoration.css。要知道有效的相对URL,您需要知道JSF页面和CSS文件的绝对URL,并从中提取一个。

假设您的CSS文件实际位于http://example.com/context/styles/decoration.css,那么您需要删除前导斜杠,以使其相对于当前上下文(即page.jsp的上下文):

<link rel="stylesheet" type="text/css" href="styles/decoration.css" />

1
谢谢。这正是我正在寻找的。 - kravemir
6
这并不是正确的Facelets方式,更像是JSP的方式。 - BalusC

2

这些答案帮助我解决了相同的问题。虽然我的问题更加复杂,因为我正在使用SASS和GULP。

我不得不更改(请注意“\”在#前面。可能是来自gulp的副作用:

 <h:outputStylesheet library="my_theme" name="css/default.css"/>  

 background: $blue url("\#{resource['my_theme/images/background-homepage-h1.png']}");

0

resourcehandlers.UnmappedResourceHandler可以帮助将JSF资源映射到URL模式/javax.faces.resource/*。

对我来说,在faces-config.xml中的这两个xml配置: org.omnifaces.resourcehandler.UnmappedResourceHandler

以及在web.xml中:

<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>/javax.faces.resource/*</url-pattern>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

帮助处理CSS和图片。


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