何时使用 <ui:include>、标记文件、复合组件和/或自定义组件?

Posted

技术标签:

【中文标题】何时使用 <ui:include>、标记文件、复合组件和/或自定义组件?【英文标题】:When to use <ui:include>, tag files, composite components and/or custom components? 【发布时间】:2011-10-12 21:56:48 【问题描述】:

我最近开始将 JSF 2.0 与 Facelets 一起使用,并且对知道现有 &lt;ui:include&gt; 和 Facelets 1.x 提供的其他模板技术的新复合组件感到困惑。

这些方法之间有什么区别?从功能上讲,它们似乎提供了大致相同的功能:&lt;ui:param&gt; vs &lt;cc:attribute&gt;&lt;ui:insert&gt;+&lt;ui:define&gt; vs 标记文件,重用现有模板。在复合组件的情况下,除了语法和清晰的接口规范之外还有什么?性能会有所不同吗?

【问题讨论】:

【参考方案1】:

这些方法有什么区别?

Facelet 模板

如果您想将主页布局片段拆分为可重复使用的模板,请使用 Facelet 模板(如 &lt;ui:composition&gt;&lt;ui:include&gt;&lt;ui:decorate&gt;)。例如。页眉、菜单、内容、页脚等。

例子:

How to include another Xhtml in XHTML using JSF 2.0 Facelets? What is the real conceptual difference between ui:decorate and ui:include? How to customize h:head when using ui:composition template? How to change head elements of a page when using ui:composition How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)

Facelet 标记文件

如果您想拥有一组可重用的组件以防止/最小化代码重复,请使用 Facelet 标记文件。例如。一组标签+输入+消息组件。与复合组件的主要区别在于,Facelet 标记文件的输出不代表单个UIComponent,并且在某些情况下,当复合组件不够用时,它可能是唯一的解决方案。通常,拥有一个 &lt;ui:include&gt; 和一个或多个 &lt;ui:param&gt; 传递托管 bean 属性(因此不是硬编码值)是包含文件可以更好地成为标记文件的信号。

例子:

How to create a custom Facelets tag? How to make a grid of JSF composite component? How to create a composite component for a datatable column? Primefaces outputLabel for composite component

复合组件

如果您想使用纯 XML 创建具有单一职责的单一且可重用的自定义 UIComponent,请使用复合组件。这样的复合组件通常由一堆现有组件和/或 HTML 组成,并作为单个组件物理呈现,并且应该绑定到单个 bean 属性。例如。代表单个 java.time.LocalDate 属性的组件,由代表日、月和年的 3 个依赖 &lt;h:selectOneMenu&gt; 组件,或者将 &lt;p:fileUpload&gt;&lt;p:imageCropper&gt; 组合成单个 &lt;my:uploadAndCropImage&gt; 的组件,引用单个自定义 com.example.Image 实体作为财产。

例子:

Our Composite Component wiki page The BalusC Code: Composite Component with multiple input fields Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime Select all items in Multiple SelectManyCheckBox with dynamic ids Extending JSF commandLink component Avoiding duplicate ids when reusing facelets compositions in the same naming container

自定义组件

当由于标准/可用组件集缺乏支持而无法使用 Facelet 标记文件或复合组件实现功能时,请使用自定义组件。通常,当您想要对解码和/或编码进行高度控制和/或定制时,并且还为最终用户提供相对容易地扩展/覆盖解码和/或编码的可能性。在PrimeFaces和OmniFaces等开源组件库的源代码中随处可见示例。

标签处理程序

当您想要控制 JSF 组件树的构建而不是呈现 HTML 输出时,您应该使用标签处理程序而不是组件。

例子:

Custom Facelet component in JSF How can I access the content of something created with <ui:define> programmatically? Conditional render in tagfile depending on whether the attribute is specified or not Performing a redirect, when conversion / validation associated with query parameters fails

示例项目

以下是一些利用上述所有技术的示例项目。

Java EE Kickoff App (templates - includes - tagfiles - composite) OmniFaces Showcase (templates - includes - tagfiles - composite)

性能会有所不同吗?

从技术上讲,性能问题可以忽略不计。应根据具体的功能需求和实现的最终抽象程度、可重用性和可维护性来做出选择。每种方法都有其明确的目的和局限性。

然而,复合组件在构建/恢复视图期间(特别是:在保存/恢复视图状态期间)确实有很大的开销。而且,在旧版本的 Mojarra 中,复合组件在分配默认值时存在性能问题,这已经从 2.1.13 开始修复。此外,当 &lt;cc:attribute method-signature&gt; 用于方法表达式时,Mojarra 有一个 memory leak,基本上整个组件树在 HTTP 会话中被重新引用,这是自 2.1.29 / 2.2.8 以来修复的。在旧的 2.1 版本中可以绕过内存泄漏,如下所示:

<context-param>
    <param-name>com.sun.faces.serializeServerState</param-name>
    <param-value>true</param-value>
</context-param>

或者在旧的 2.2 版本中如下:

<context-param>
    <param-name>javax.faces.SERIALIZE_SERVER_STATE</param-name>
    <param-value>true</param-value>
</context-param>

不过,当您拥有相对“很多”的复合组件,并且您将 javax.faces.STATE_SAVING_METHOD 设置为 client 时,性能将会很痛苦。如果您只想要简单的包含文件或标记文件已经可以实现的基本功能,请不要滥用复合组件。不要以易于配置(阅读:不需要*.taglib.xml 文件)为借口偏爱复合组件而不是标记文件。

当使用 Mojarra 2.2.10 或更早版本时,不要忘记为生产模式禁用相对较短的 Facelets 刷新周期:

<context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>-1</param-value>
</context-param>

不要将此设置用于开发,否则您必须重新启动整个服务器才能反映 Facelets 文件中的更改! Mojarra 2.2.11 和更新版本,当 javax.faces.PROJECT_STAGE 未设置为 Development 时,MyFaces 已经默认为 -1

【讨论】:

为什么要渲染 1 个组件(复合组件)而不是 3 个(facelet 标记文件)?我的意思是,在阳光明媚的日子里,你可能会感觉像 1 而不是 3……但我想这背后还有别的东西。在您的示例中,您正在扩展 UINamingContainer ...这可能是选择 cc 的原因之一(以便能够覆盖一些 jsf 实现特定的功能)? 标签文件应该被视为一种包含。复合组件应被视为真实组件。复合组件需要实现NamingContainer,否则当同一个组件被多次重用时,最终会出现重复ID问题。 @BalusC 假设我有一堆 HTML 和 JSF,它们创建了一个“块”,允许我添加或删除地址(及其所有属性:街道、号码、城市等)。我需要在 2 或 3 页中使用相同的块。这是否属于您对复合组件的描述? @Rinaldo:我想我会使用带有动态填充组件 ID 的标记文件,如 ***.com/questions/5713718/… 中所示。 IMO,如果可以使用标记文件完成,请使用它。如果无法使用标记文件完成,请使用复合材料。如果您需要多个组件来操作单个属性(不是地址,而是例如应该放在单个属性中的街道名称+门牌号),那么复合组件将是唯一的解决方案。 @Tarik:与标签文件相比,复合材料有很多开销。换句话说:性能差。仅当您需要基于一组密切相关的现有组件创建单个自定义 UI 组件时才使用它。这不能用标记文件来完成。例如,ZEEF.com 只有一个组合:a.o. 中使用的上传/下载/裁剪图像一体化的东西。页面图片、头像、链接块头、图片块等。它只绑定到bean中的Image属性。

以上是关于何时使用 <ui:include>、标记文件、复合组件和/或自定义组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ui:include 和参数?

有条件地渲染一个 <ui:include>

20170906 - XML基础 - A

何时使用 Mono<List<Object>> 以及何时使用 Flux<Object> 用于 RestController 方法

ui:include的路径无效

何时在 Rails 视图中使用 @ 以及何时使用符号?