为何<c:if test="#{MyBean.myFunction()}">在XHTML文件中重复渲染?
问题分析与解决方案
核心原因
JSTL的<c:if>和JSF组件的执行时机完全不同:
<c:if>是在视图构建阶段执行的,这个阶段负责生成JSF组件树。如果页面因为某些情况(比如Bean是请求作用域、ajax请求触发视图重建、页面包含动态组件导致树反复构建),<c:if>里的表达式会被多次求值,进而多次触发myFunction(),导致内部组件重复渲染。<h:outputText>的value表达式则是在视图渲染阶段执行的,这个阶段是组件树构建完成后才触发,通常一次请求只会执行一次渲染,所以方法仅执行一次。
解决办法
把JSTL的<c:if>替换成JSF自带的rendered属性,直接绑定到需要控制的组件上:
<div id="myId" class="someClass" rendered="#{MyBean.myFunction()}"> <h4>someText</h4> </div>
如果是原生HTML组件不支持rendered属性,就用<h:panelGroup>包裹,把属性加在面板组上:
<h:panelGroup rendered="#{MyBean.myFunction()}"> <div id="myId" class="someClass"> <h4>someText</h4> </div> </h:panelGroup>
额外优化建议
- 把Bean换成
@ViewScoped(JSF 2及以上版本支持),这样组件树在整个视图生命周期内只会构建一次,避免因作用域问题导致的重复执行。 - 避免在EL表达式里写有副作用的方法,比如修改数据、调用远程接口。如果只是做判断逻辑,建议把结果缓存到Bean的字段中,示例如下:
@ViewScoped public class MyBean implements Serializable { private boolean shouldRender; @PostConstruct public void init() { shouldRender = calculateRenderCondition(); // 仅执行一次初始化 } private boolean calculateRenderCondition() { // 原myFunction()的判断逻辑 return true; } public boolean isShouldRender() { return shouldRender; } }
页面上直接使用缓存后的字段:
<h:panelGroup rendered="#{MyBean.shouldRender}"> <div id="myId" class="someClass"> <h4>someText</h4> </div> </h:panelGroup>
内容的提问来源于stack exchange,提问作者boycott-nestle
相关产品推荐
相关产品推荐

