【问题标题】:When to use <ui:include>, tag files, composite components and/or custom components?何时使用 <ui:include>、标记文件、复合组件和/或自定义组件?
【发布时间】: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 标记文件,重用现有模板。在复合组件的情况下,除了语法和清晰的接口规范之外还有什么?性能会有所不同吗?

【问题讨论】:

    标签: jsf-2 facelets custom-component composite-component tagfile


    【解决方案1】:

    这些方法有什么区别?

    Facelet 模板

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

    例子:

    Facelet 标记文件

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

    例子:

    复合组件

    如果您想使用纯 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 实体作为财产。

    例子:

    自定义组件

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

    标签处理程序

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

    例子:

    示例项目

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


    性能会有所不同吗?

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

    然而,复合组件在构建/恢复视图期间(特别是:在保存/恢复视图状态期间)确实有很大的开销。而且,在旧版本的 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 的标记文件,如 stackoverflow.com/questions/5713718/… 中所示。 IMO,如果可以使用标记文件完成,请使用它。如果无法使用标记文件完成,请使用复合材料。如果您需要多个组件来操作单个属性(不是地址,而是例如应该放在单个属性中的街道名称+门牌号),那么复合组件将是唯一的解决方案。
    • @Tarik:与标签文件相比,复合材料有很多开销。换句话说:性能差。仅当您需要基于一组密切相关的现有组件创建单个自定义 UI 组件时才使用它。这不能用标记文件来完成。例如,ZEEF.com 只有一个组合:a.o. 中使用的上传/下载/裁剪图像一体化的东西。页面图片、头像、链接块头、图片块等。它只绑定到bean中的Image属性。
    猜你喜欢
    • 2011-07-21
    • 2016-12-05
    • 2011-08-07
    • 2013-05-25
    • 2017-09-01
    相关资源
    最近更新 更多