热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

高并发Nginx+LuaOpenResty系列(8)——Lua模版渲染

模版渲染动态web网页开发是Web开发中一个常见的场景,比如像京东商品详情页,其页面逻辑是非常复杂的,需要使用模板技术来实现。而Lua中也有许多模板引擎,如目前京东在使用的lua
模版渲染

动态web网页开发是Web开发中一个常见的场景,比如像京东商品详情页,其页面逻辑是非常复杂的,需要使用模板技术来实现。而Lua中也有许多模板引擎,如目前京东在使用的lua-resty-template,可以渲染很复杂的页面,借助LuaJIT其性能也是可以接受的。


如果学习过JavaEE中的servlet和JSP的话,应该知道JSP模板最终会被翻译成Servlet来执行;而lua-resty-template模板引擎可以认为是JSP,其最终会被翻译成Lua代码,然后通过ngx.print输出。


而lua-resty-template和大多数模板引擎是类似的,大体内容有:
模板位置:从哪里查找模板;
变量输出/转义:变量值输出;
代码片段:执行代码片段,完成如if/else、for等复杂逻辑,调用对象函数/方法;
注释:解释代码片段含义;
include:包含另一个模板片段;
其他:lua-resty-template还提供了不需要解析片段、简单布局、可复用的代码块、宏指令等支持。

下载lua-resty-template

wget https://github.com/bungle/lua-resty-template/archive/v1.9.tar.gz
tar -xvzf v1.9.tar.gz

解压后可以看到lib/resty下面有一个template.lua,这个就是我们所需要的,在template目录中还有两个lua文件,将这两个文件复制到/usr/openResty/lualib/resty中即可。
接下来就可以通过如下代码片段引用了:

local template = require("resty.template")

模版位置

我们需要告诉lua-resty-template去哪儿加载我们的模块,此处可以通过set指令定义template_location、template_root或者从root指令定义的位置加载。
我们可以在openResty.conf配置文件的server部分定义

# first match ngx location
set $template_location "/templates";
# then match root read file
set $template_root "/usr/openResty/templates";

也可以通过在server部分定义root指令

root /usr/openResty/templates;

其顺序是

local function load_ngx(path)
    local file, location = path, ngx_var.template_location
    if file:sub(1)  == "/" then file = file:sub(2) end
    if location and location ~= "" then
        if location:sub(-1) == "/" then location = location:sub(1, -2) end
        local res = ngx_capture(location .. '/' .. file)
        if res.status == 200 then return res.body end
    end
    local root = ngx_var.template_root or ngx_var.document_root
    if root:sub(-1) == "/" then root = root:sub(1, -2) end
    return read_file(root .. "/" .. file) or path
end
  1. 通过ngx.location.capture从template_location查找,如果找到(状态为为200)则使用该内容作为模板;此种方式是一种动态获取模板方式;
  2. 如果定义了template_root,则从该位置通过读取文件的方式加载模板;
  3. 如果没有定义template_root,则默认从root指令定义的document_root处加载模板。


    此处建议首先template_root,如果实在有问题再使用template_location,尽量不要通过root指令定义的document_root加载,因为其本身的含义不是给本模板引擎使用的。


    接下来定义模板位置
mkdir /usr/openResty/templates
mkdir /usr/openResty/templates2

openResty.conf配置文件

# first match ngx location
set $template_location "/templates";
# then match root read file
set $template_root "/usr/openResty/templates";

location /templates {
    internal;
    alias /usr/openResty/templates2;
}

首先查找/usr/openResty/template2,找不到会查找/usr/openResty/templates。
然后创建两个模板文件

vi /usr/openResty/templates2/t1.html

内容为

template2
vi /usr/example/templates/t1.html

内容为

 template1

test_temlate_1.lua

local template = require("resty.template")
template.render("t1.html")

openResty.conf配置文件

location /lua_template_1 {
    default_type 'text/html';
    lua_code_cache on;
    content_by_lua_file /usr/openResty/lua/test_template_1.lua;
}

访问如http://127.0.0.1/lua_template_1将看到template2输出。然后rm /usr/openResty/templates2/t1.html,reload nginx将看到template1输出。
接下来的测试我们会把模板文件都放到/usr/openResty/templates下。

API

使用模板引擎目的就是输出响应内容;主要用法两种:直接通过ngx.print输出或者得到模板渲染之后的内容按照想要的规则输出。

test_template_2.lua

local template = require("resty.template")
--是否缓存解析后的模板,默认true
template.caching(true)
--渲染模板需要的上下文(数据)
local cOntext= {title = "title"}
--渲染模板
template.render("t1.html", context)

ngx.say("
") --编译得到一个lua函数 local func = template.compile("t1.html") --执行函数,得到渲染之后的内容 local cOntent= func(context) --通过ngx API输出 ngx.say(content)

常见用法即如下两种方式:要么直接将模板内容直接作为响应输出,要么得到渲染后的内容然后按照想要的规则输出。

openResty.conf配置文件

location /lua_template_2 {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/openResty/lua/test_template_2.lua;  
} 
使用示例

test_template_3.lua

local template = require("resty.template")

local cOntext= {
  title = "测试",
  name = "张三",
  description = "",
  age = 20,
  hobby = {"电影", "音乐", "阅读"},
  score = {语文 = 90, 数学 = 80, 英语 = 70},
  score2 = {
    {name = "语文", score = 90},
    {name = "数学", score = 80},
    {name = "英语", score = 70},
  }
}

template.render("t3.html", context)

模板文件/usr/openResty/templates/t3.html


  
    
  
  
    {# 不转义变量输出 #}
    姓名:{* string.upper(name) *}
{# 转义变量输出 #} 简介:{{description}}
{# 可以做一些运算 #} 年龄: {* age + 1 *}
{# 循环输出 #} 爱好: {% for i, v in ipairs(hobby) do %} {% if i > 1 then %},{% end %} {* v *} {% end %}
成绩: {% local i = 1; %} {% for k, v in pairs(score) do %} {% if i > 1 then %},{% end %} {* k *} = {* v *} {% i = i + 1 %} {% end %}
成绩2: {% for i = 1, #score2 do local t = score2[i] %} {% if i > 1 then %},{% end %} {* t.name *} = {* t.score *} {% end %}
{# 中间内容不解析 #} {-raw-}{(file)}{-raw-}

{(include_file)}:包含另一个模板文件;
{* var *}:变量输出;
{{ var }}:变量转义输出;
{% code %}:代码片段;
{# comment #}:注释;
{-raw-}:中间的内容不会解析,作为纯文本输出;
模板最终被转换为Lua代码进行执行,所以模板中可以执行任意Lua代码。

openResty.conf配置文件

location /lua_template_3 {
    default_type 'text/html';
    lua_code_cache on;
    content_by_lua_file /usr/openResty/lua/test_template_3.lua;
}

访问如http://127.0.0.1/lua_template_3进行测试。
基本的模板引擎使用到此就介绍完了。


推荐阅读
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • python模块之正则
    re模块可以读懂你写的正则表达式根据你写的表达式去执行任务用re去操作正则正则表达式使用一些规则来检测一些字符串是否符合个人要求,从一段字符串中找到符合要求的内容。在 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
  • 本文介绍了一种使用 JavaScript 计算两个日期之间时间差的方法。该方法支持多种时间格式,并能返回秒、分钟、小时和天数等不同精度的时间差。 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 检查在所有可能的“?”替换中,给定的二进制字符串中是否出现子字符串“10”带 1 或 0 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 利用 JavaScript 和 Node.js 验证时间的有效性
    本文探讨了如何使用 JavaScript 和 Node.js 验证时间的有效性。通过编写一个 `isTime` 函数,我们可以确保输入的时间格式正确且有效。该函数利用正则表达式匹配时间字符串,检查其是否符合常见的日期时间格式,如 `YYYY-MM-DD` 或 `HH:MM:SS`。此外,我们还介绍了如何处理不同时间格式的转换和验证,以提高代码的健壮性和可靠性。 ... [详细]
  • 短视频app源码,Android开发底部滑出菜单首先依赖三方库implementationandroidx.appcompat:appcompat:1.2.0im ... [详细]
  • C语言编写线程池的简单实现方法
    2019独角兽企业重金招聘Python工程师标准好文章,一起分享——有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带 ... [详细]
  • 字符串学习时间:1.5W(“W”周,下同)知识点checkliststrlen()函数的返回值是什么类型的?字 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
author-avatar
好学的程序员
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有