80行Lua代码实现一个满足基本要求的模版引擎,以下内容仅供学习交流,未经严格考虑与测试,切勿用于生产环境。
ltemplate.lua
local insert = table.insert
local remove = table.remove
local concat = table.concat
local format = string.format
local loaded = {}
local partten = "(.-){#([^#].-[^#])#}()"
local content = {}
local cur_content = nil
local function ob_start()
cur_content = {}
insert(content, cur_content)
end
local function ob_get_clean()
local ret = concat(cur_content)
remove(content)
cur_content = content[#content]
return ret
end
local function echo(value)
insert(cur_content, value)
end
local function include(path, params)
local bitcode = loaded[path]
if not bitcode then
local fp = io.open(path, "rb")
local template = fp:read('*a')
fp:close()
local results = {}
local last_endpos = 0
for outside, inside, endpos in template:gmatch(partten) do
insert(results, format("echo(%q)", outside))
insert(results, inside)
last_endpos = endpos
end
insert(results, format("echo(%q)", template:sub(last_endpos)))
results = concat(results, "\n")
bitcode = assert(loadstring(results))
loaded[path] = bitcode
end
local env = {
include = include,
echo = echo,
ob_start = ob_start,
ob_get_clean = ob_get_clean
}
setmetatable(env, {__index = function(tb, k)
return params[k] or _G[k]
end})
setfenv(bitcode, env)
bitcode()
end
for i = 1, 100000 do
ob_start()
include(arg[1], {
params = {
a = '1234',
b = '4321'
}
})
ob_get_clean()
end
master.html
child's personal page - 开源中国社区
<script type="text/java script" src="/js/2012/jquery-1.7.1.min.js">
<script type="text/java script" src="/js/2012/jquery.form.js">
<script type="text/java script" src="/js/2011/fancybox/jquery.fancybox-1.3.4.pack.js">
<script type="text/java script" src="/js/2012/poshytip/jquery.poshytip.min.js">
<script type="text/java script" src="/js/2011/oschina.js ver=20121007">
<script type="text/java script" src="/js/2012/less-1.3.0.min.js">
<script type="text/java script" src="/js/scrolltopcontrol.js">
<script type='text/java script' src='/js/jquery/jquery.atwho.js ver=2013112501'>
{# echo(header) #}
{# echo(content) #}
temp.html,继承master.html
{# ob_start() #}
<script>
alert("hello World")
{# local header = ob_get_clean() #}
{# ob_start() #}
{# for k, v in pairs(params) do #}
{# echo(k) #} |
{# echo(v) #} |
{# end #}
{# local content = ob_get_clean() #}
{# include('master.html', {header = header, content = content}) #}
循环十万次测试渲染速度(阿里云最便宜一款vps)
[root@AY130801221248587d02Z ~]# time lua ltemplate.lua temp.html
real 0m1.867s
user 0m1.862s
sys 0m0.004s
总结
由此可见渲染的速度还是非常快的,可以将此原型用于嵌入式设备中的页面上(用大量js实现的嵌入式设备页面兼容性不好)。而且嵌入式设备的界面需要简单明确,所以也不用太丰富的模版功能。
原理很简单:
1.用lua版的正则把模版内{#与#}之间的内容挖出来,原样输出成lua代码,其它部分则生成使用echo打印到某个缓冲区的lua代码。
2.将这个生成出来的代码使用loadstring编译。
3.通过setfenv实现loadstring后的模拟环境配置(用以提供模版内使用的echo,ob_start等函数,以及传入的参数)
4.执行这个编译后的函数即可。
推荐阅读: