发表时间:2011-03-24
最后修改:2011-03-24
Velocity模板渲染页面性能优化
对于页面性能优化这块,尤其是velocity宏的使用,如何使用宏,怎么样将宏的使用发挥到极致,达到更大的性能的提示,
我的建议如下:
1:一般我们不推荐使用宏,因为宏每次都要要JJT解析,然后才能再执行;
2:使用宏能达到最好的性能情况下,非常安全的,一般在页面有些信息是用户输入的情况
使用会比较好。
下面我针对他的一些意见和我们项目的一些情况,整理了一些关于宏使用的一些技术知识
一:我们如何定义宏和使用宏?
1:定义宏和使用宏
#macro指令用于定义一个VTL模板的重复代码块——宏。下面是一个简单的定义宏的例子:
#macro( d )
<tr><td></td><tr>
#end
这段代码定义了一个宏,名字为d,没有参数。下面是使用这个宏的代码:
#d()
Velocity在遇到#d()的时候,会用"<tr><td></td></tr>"替代上面的#d()这一行。
宏的参数:
宏也可以带参数,而且是任意多个参数。不过,宏定义时有几个参数,宏调用时就要提供同样数目的参数。
#macro( d $name)
<tr><td>$name</td></tr>
#end
#d("name1")
宏的参数可以是以下VTL元素中的任意一种:引用、字符串字面值、数值字面值、整数范围(比如[1 .. 10]、[$start .. $end])、数组、布尔值true或者false。
宏的参数可以是方法,那么下面这个例子,需要特别注意:
#macro(test $a)
$a $a $a
#end
#test($foo.bar())
上面这个例子中,$foo.bar()将会被调用3次,而不是一次。
内联的宏
当宏是在一个Velocity模板中定义时,这个宏(是inline的)只能被该模板使用,同一个网站下的其他模板是不能用的。如果是在一个Velocity宏模板库中定义的宏,就可以被任何同一网站下的模板使用。
和宏有关的一些Velocity属性
velocimacro.library——用逗号分隔的一组文件名,是Velocity宏模板库。默认值是VM_global_library.vm
velocimacro.permissions.allow.inline——宏是否可以在一个普通模板中定义。默认值是false。
velocimacro.permissions.allow.inline.to.replace.global——是否允许模板中的宏覆盖library中的宏。默认值是false。
velocimacro.permissions.allow.inline.local.scope——一个在普通模板中定义的宏,是否允许其他模板使用。默认是false。
velocimacro.context.localscope——在一个宏里通过#set()修改了context,此修改是否仅仅对这个宏自身,而不是永久性修改了context。默认值是false。
velocimacro.library.autoreload——Velocity宏模板库修改之后,是否自动重新加载。默认值是false。debug时可以设置为true,发布时设置为false。
其他一些注意点
宏必须在第一次使用它之前定义。当#Parse()一个模板文件时,尤其要注意这一点。
二: 引入指令和#Stop指令
#Include和#Parse都是用于将本地文件引入当前文件的指令,而且被引入的文件必须位于TEMPLATE_ROOT。这两者之间有一些区别。
#Include
被#Include引入的文件,其内容不会被Velocity引擎解析,所以这些文件应该是静态模板,即不含有VTL的模板。使用#Include()指令时,参数是被双引号括起来的文件名或者是表示文件名的变量。如果有多个文件,以逗号隔开即可。比如#Include("a.gif", "b.html", $file)。
#Parse
#Parse用来在当前模板中引入并执行另一个(本地的)模板——可以是静态的,也可以是动态的——并把结果嵌入到当前位置。#Parse()指令的参数,可以是一个双引号括起来的文件名,也可以是一个变量,但是它不能接受多个参数。
被#Parse引入的文件仍然可以使用#Parse指令。在velocity.properties文件中有一个属性directive.parse.max.depth,默认值是10,它指定了#Parse嵌套的最大层次。既然#Parse嵌套是允许的,#Parse递归也是允许的。
假如a.vm #Parse b.vm,那么a.vm中定义的变量$v,在b.vm中可以随便使用。如果b.vm也定义了$v,那么b.vm中用到的将会是自己的$v,而不是a.vm中的$v。
#Stop
#Stop指令会停止模板引擎的执行,并返回。这在debug中比较有用。
三:#include和#parse到底有什么区别?
parse方法是解析文件,宏是全局方法(调用该方法返回数据),调用方法比较快。
#parse是动态加载的,需要***.vm中的宏定义在 velocity启动的时候就应该得到加载
#include
1.可包含本地文件(不包含VTL)
2.文件内容不经过template engine处理
3.出于安全性的考虑,此文件只能位于TEMPLATE_ROOT目录下
#parse
1.可以引入包含VTL的模板
2.任何模板文件只能位于TEMPLATE_ROOT目录下
比如macro.vm里面添加
#macro (static $src)
$env.getProperties("app.static")/$src
#end
#macro (function_name param_names)
// code...
#end
这样使用 #static("...")方法调用
parse与include的区别在于,若包含的文件中有Velocity脚本标签,将会进一步解析,而include将原样显示。
实例1:
#macro(macroName)#end 脚本函数(宏)调用,不推荐在界面模板中大量使用。
如:在使用EasyJWeb Tools快速生成的添删改查示例中,可以点击列表的标题栏进行升降排序显示,这是我们在EasyJWeb应用中经常看到的一个排序状态显示的模板内容。
函数(宏)定义,一般放在最前面
#macro(orderPic $type)
#if ($orderField.equals($type))
<img src="/images/ico/${orderType}.gif">
#end
#end
具体的调用如:<font color="#FFFFFF">头衔#orderPic("title")</font>
实例2:
包含文件#inclue("模板文件名")或#parse("模板文件名")
主要用于处理具有相同内容的页面,比如每个网站的顶部或尾部内容。
使用方法,可以参考EasyJF开源Blog及EasyJF开源论坛中的应用!
如:#parse("/blog/top.html")或#include("/blog/top.html")
四: Velocity宏使用中需要注意的问题
1、变量输出要带上静止修饰符,避免模板直接空指针报错
举例 :$var错误,$!var正确
如果变量referce与html代码连在一起,需要加上{}
举例 :$! {var}gadsgaggaggas
2、Macro的使用
由于宏存在着安全和性能问题,并且会影响模板的可读性,在模板中不推荐使用宏。
3、变量引用不要加引号,避免重复转义举例:
$stringUtil.equals("$text", "&") 错误
$stringUtil.equals($text, "&") 正确
4、关于velocity的版本
目前推荐使用 velocity 1.6.1,因为以往velocity版本往往会出现向下不兼容的情况 版本升级需要经过架构部门(校长)的充分论证和评估才能实施。
5、Velocity的安全性
为避免VELOCITY页面渲染后产生XSS漏洞,应用应该采用校长的安全方案在应用中引入toolkit-service-velocity.1.5 包。
XSS的过滤,使用velocity提供的xss过滤功能
1)对于html,可以使用 #SHTML($html)2)对于JS,可以用 #SJS($js)
3)对于xml,可以使用#SXML($xml)
4)屏蔽转义,可以使用#SLITERAL()
上述宏,因为非常消耗CPU,因此如果不含特定内容,不到万不得已,不得使用。
五:总结:
1:模块化的东西最好不要放入循环当中,parse或者宏都只是加载一段公共的代码片段而已,循环只是对数据处理来说的;
2:用parse和宏区别不大,parse解析的是一个vm文件,宏返回一段vm代码,优化的过程不是说使用parse或者宏,而是文件或宏里面的数据处理代码,这些代码才有高低效率之分,parse和宏只是在不同场景下不同的使用方法而已。
参考文章:http://people.apache.org/~henning/velocity/html/ch07.html