80行lua代码实现一个满足基本要求的模版引擎

jopen 10年前

以下内容仅供学习交流,未经严格考虑与测试,切勿用于生产环境。

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html lang='zh-CN' xml:lang='zh-CN' xmlns='http://www.w3.org/1999/xhtml'>  <head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>    <meta http-equiv="Content-Language" content="zh-CN"/>    <meta name="robots" content="index, follow" />    <link rel="shortcut icon" type="image/x-icon" href="/img/favicon.ico" />    <title> child&apos;s personal page - 开源中国社区</title>        <link rel="stylesheet/less" href="http://my.oschina.net/lostchild/styles.less?ver=20131219&date=20131110185237" type="text/css" media="screen" />    <link rel="stylesheet" href="/js/2012/poshytip/tip-yellowsimple/tip-yellowsimple.css" type="text/css" />    <link rel="stylesheet" type="text/css" href="/js/2011/fancybox/jquery.fancybox-1.3.4.css" media="screen" />    <script type="text/javascript" src="/js/2012/jquery-1.7.1.min.js"></script>    <script type="text/javascript" src="/js/2012/jquery.form.js"></script>    <script type="text/javascript" src="/js/2011/fancybox/jquery.fancybox-1.3.4.pack.js"></script>    <script type="text/javascript" src="/js/2012/poshytip/jquery.poshytip.min.js"></script>    <script type="text/javascript" src="/js/2011/oschina.js?ver=20121007"></script>    <script type="text/javascript" src="/js/2012/less-1.3.0.min.js"></script>    <script type="text/javascript" src="/js/scrolltopcontrol.js"></script>    <script type='text/javascript' src='/js/jquery/jquery.atwho.js?ver=2013112501'></script>    <link rel="stylesheet" type="text/css" href="/js/jquery/jquery.atwho.css" />    <link rel="alternate" type="application/rss+xml" title="lostchild最新博客" href="http://my.oschina.net/lostchild/rss" />    <link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://my.oschina.net/action/xmlrpc/rsd?space=1397642" />    <link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://my.oschina.net/action/xmlrpc/wlwmanifest?space=1397642" />    {# echo(header) #}  </head>  <body>  {# echo(content) #}  <body>  </html>

temp.html,继承master.html

{# ob_start() #}  <script>   alert("hello World")  </script>  {# local header = ob_get_clean() #}      {# ob_start() #}  <table>  {# for k, v in pairs(params) do #}  <tr>   <td>{# echo(k) #}</td>   <td>{# echo(v) #}</td>  </tr>  {# end #}  </table>  {# 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.执行这个编译后的函数即可。

来自:http://my.oschina.net/visualgui823/blog/186588