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

Stella981
• 阅读 696

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

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="https://my.oschina.net/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="https://my.oschina.net/js/2012/poshytip/tip-yellowsimple/tip-yellowsimple.css" type="text/css" />
  <link rel="stylesheet" type="text/css" href="https://my.oschina.net/js/2011/fancybox/jquery.fancybox-1.3.4.css" media="screen" />
  <script type="text/javascript" src="https://my.oschina.net/js/2012/jquery-1.7.1.min.js"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/2012/jquery.form.js"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/2011/fancybox/jquery.fancybox-1.3.4.pack.js"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/2012/poshytip/jquery.poshytip.min.js"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/2011/oschina.js?ver=20121007"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/2012/less-1.3.0.min.js"></script>
  <script type="text/javascript" src="https://my.oschina.net/js/scrolltopcontrol.js"></script>
  <script type='text/javascript' src='/js/jquery/jquery.atwho.js?ver=2013112501'></script>
  <link rel="stylesheet" type="text/css" href="https://my.oschina.net/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.执行这个编译后的函数即可。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
Golang注册Eureka的工具包goeureka发布
1.简介提供Go微服务客户端注册到Eureka中心。点击:github地址(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2FSimonWang00%2Fgoeureka),欢迎各位多多star!(已通过测试验证,用于正式生产部署)2.原理
Wesley13 Wesley13
3年前
MySQL数据库InnoDB存储引擎Log漫游(1)
作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这